In-depth discussion on MFC message loop and message pump (I)

Source: Internet
Author: User

First, we should understand the message loop (: getmessage,: peekmessage) and message pump (cwinthread: pumpmessage) of MFC) messages in windows are different from messages in MFC. In an MFC application (the application class is inherited based on cwinthread), there must be a message loop. Its function is to read messages from the message queue of the application, and send it out (: dispatchmessage ). Message routing refers to the window to which the system (user32.dll) delivers the message after the message is sent, and the transfer of the message between windows in the future.

Messages are divided into queue messages (message queues that enter the thread) and non-queue messages (message queues that do not enter the thread ). For queue messages, the most common messages are those triggered by the mouse and keyboard, such as wm_mousermove and wm_char, and wm_paint, wm_timer, and wm_quit. When a mouse or keyboard event is triggered, the corresponding mouse or keyboard driver converts the event to a message and then delivers it to the system message queue, the Windows system is responsible for adding messages to the Message Queue of the corresponding thread, so there is a message loop (reading and dispatching messages from the Message Queue ). There is also a non-queue message, which bypasses the system queue and message queue and directly sends the message to the window process. For example, when a user activates a window system, wm_activate, wm_setfocus, and wm_setcursor are sent. Wm_create message is sent when the window is created. Later, you will see that the design of Ms makes sense and its complete implementation mechanism.

The message loop and message Pump of MFC are described here. First, let's take a look at how the program enters the message loop at startup:

_ Twinmain-> afxwinmain-> afxwininit-> cwinthread: initapplication-> cwinthread: initinstance-> cwinthread: Run

The message loop of non-dialog programs starts from the run of the cwinthread...

Part 1: Message Loop Mechanism of Non-dialog programs.

// Thrdcore. cpp
// Main running routine until thread exits
Int cwinthread: Run ()
{
Assert_valid (this );

// For tracking the idle time state
Bool bidle = true;
Long lidlecount = 0;

// Acquire and dispatch messages until a wm_quit message is wrongly ed.
For (;;)
{
// Phase1: Check to see if we can do Idle Work
While (bidle &&
! : Peekmessage (& m_msgcur, null, pm_noremove ))
{
// Call onidle while in bidle state
If (! Onidle (lidlecount ++ ))
Bidle = false; // assume "no idle" state
}

// Phase2: pump messages while available
Do
{
// Pump message, but quit on wm_quit
If (! Pumpmessage ())
Return exitinstance ();

// RESET "no idle" state after pumping "normal" Message
If (isidlemessage (& m_msgcur ))
{
Bidle = true;
Lelecount = 0;
}

} While (: peekmessage (& m_msgcur, null, pm_noremove ));
} // Infinite loop. The exit condition is to receive the wm_quit message.

Assert (false); // not reachable
}

This is an infinite loop. Its exit condition is to receive the wm_quit message:

If (! Pumpmessage ())
Return exitinstance ();

In pumpmessage, if the message wm_quit is received, false is returned. Therefore, the exitinstance () function is executed, and the exit code of the program is returned. Therefore, to exit a program, you only need to call the function in the code.

Void postquitmessage (INT nexitcode ). Exit nexitcode to exit the program.

The following describes the run process of this function in two steps:

1. The first internal loop phase1. Bidle indicates whether the program is idle. It means that if the program is idle and there is no message to be processed in the message queue, the virtual function onidle is called for idle processing. In this process, the UI interface (such as the Enable and disable statuses of toolbar buttons) will be updated to delete temporary objects (such as object pointers obtained using fromhandle. For this reason, it is insecure to pass the Object Pointer obtained by fromhandle between functions because it has no persistence ). Onidle can be reloaded. You can reload it and return true to keep the message loop idle.

Note: Ms uses temporary objects for efficiency, so that the memory can be effectively used and resources can be automatically revoked when idle. There are several ways to convert a handle into an object. Generally, you declare an object OBJ first, and then bind it to a handle using obj. attatch. The generated object is permanent. You must use obj. Detach to release the object.

2. The second inner loop phase2. In this cycle, start the message pump (pumpmessage). If it is not a wm_quit message, the message pump sends the message (: dispatchmessage ). The Message destination is the window corresponding to the hwnd field in the message structure.

// Thrdcore. cpp
Bool cwinthread: pumpmessage ()
{
Assert_valid (this );

// If it is wm_quit, exit the function (return false), which causes the program to end.
If (! : Getmessage (& m_msgcur, null )){
# Ifdef _ debug
If (afxtraceflags & traceappmsg)
Trace0 ("cwinthread: pumpmessage-received wm_quit./N ");
M_ndisablepumpcount ++; // application must die
// Note: prevents calling message loop things in 'exitinstance'
// Will never be decremented
# Endif
Return false;
}

# Ifdef _ debug
If (m_ndisablepumpcount! = 0)
{
Trace0 ("error: cwinthread: pumpmessage called when not permitted./N ");
Assert (false );
}
# Endif

# Ifdef _ debug
If (afxtraceflags & traceappmsg)
_ Afxtracemsg (_ T ("pumpmessage"), & m_msgcur );
# Endif

// Process this message

If (m_msgcur.message! = Wm_kickidle &&! Pretranslatemessage (& m_msgcur ))

{
: Translatemessage (& m_msgcur); // key Conversion
: Dispatchmessage (& m_msgcur); // send a message
}
Return true;
}

?
In this step, we have a particularly important function: pretranslatemessage. This function pre-processes the message before: dispatchmessage sends the message to the window. The pretranslatemessage function is a member function of cwinthread. When you reload the function, it is in the View class or main window class. How does it access other classes? The Code is as follows:

// Thrdcore. cpp
Bool cwinthread: pretranslatemessage (MSG * PMSG)
{
Assert_valid (this );

// If it is a thread message, the processing function of the thread message will be called.
If (PMSG-> hwnd = NULL & dispatchthreadmessageex (PMSG ))
Return true;

// Walk from target to Main Window
Cwnd * pmainwnd = afxgetmainwnd ();
If (cwnd: FIG (pmainwnd-> getsafehwnd (), PMSG ))
Return true;

// In case of modeless dialogs, last chance route through main
// Window's accelerator table
If (pmainwnd! = NULL)
{
Cwnd * pwnd = cwnd: fromhandle (PMSG-> hwnd );
If (pwnd-> gettoplevelparent ()! = Pmainwnd)
Return pmainwnd-> pretranslatemessage (PMSG );
}

Return false; // no special processing
}

The above function shows that:

First, if (PMSG-> hwnd = NULL), it indicates that this is a thread message. Call cwinthread: dispatchthreadmessageex to find the message entry in the message ing table, and then call the message processing function.

Note: Generally, the postthreadmessage function is used to send messages between threads. Different from window messages, the thread ID must be specified, and the message excitation is put into the message queue of the target thread by the system; the on_thread_message (message, memberfxn) macro can be used to map the thread message and its processing function. This macro must be in the application class (inherited from cwinthread), because only the application class can process thread messages. If you use this macro in other classes (such as view classes), the message processing function of the thread message will not receive the thread message.

Second, the pretranslatemessage function of the target window of the message first obtains the message processing permission. If the function returns false, its parent window will get the message processing permission until the main window; if the function returns true (indicating that the message has been processed), you do not need to call the pretranslatemessage function of the parent class. In this way, this ensures that both the target window of the message and its parent window have the opportunity to call pretranslatemessage-preprocessing before the message is sent to the window (if the message is processed and then false is returned,-_-B ), if you want to not pass the message to the parent class for processing, return true.

Third, if the target window of the message has no parent-child relationship with the main window, call the pretranslatemessage function of the main window. Why? Step 2 shows that if the parent window of a window is not the main window, although its pretranslatemessage returns false, the main window has no chance to call the pretranslatemessage function. We know that the conversion of the acceleration key is generally in the pretranslatemessage function of the Framework Window.

I have searched for the acceleration key Conversion Processing in MFC. Only window classes such as cframewnd, cmdiframewnd, and cmdichildwnd are available. Therefore, the third step means that if the target window of the message (its parent window is not the main window, such as a non-mode dialog box) if the pre-processing of a message continues roaming (his pretranslatemessage returns false), give a chance to call the pretranslatemessage for the main window (in case he is an acceleration key message ?), In this way, the acceleration key of the main window can be ensured in non-mode dialog box.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.