First, you should clear the MFC's message loop (:: getMessage, :: PeekMessage), message pump (CWINTHREAD :: PumpMessage) and MFC's message between the windows is two different things. In the MFC application (application class based on CWINTHREAD inheritance), there must be a message loop, his role is to read the message from the application's message queue and send it (:: DispatchMessage). The message route is that the system (user32.dll) is delivered to which window is delivered to the message, and how the message is passed in the window.
The message is divided into queue messages (message queue from the thread) and non-queue messages (not entering the thread). For queue messages, the most common messages triggered by mouse and keyboard, such as WM_Mousermove, WM_CHAR, etc., for example: WM_Paint, WM_TIMER, and WM_QUIT. When the mouse, the keyboard event is triggered, the corresponding mouse or keyboard driver converts these events into the corresponding message, then transports to the system message queue, and the Windows system is responsible for adding the message to the message queue of the corresponding thread, so There is a message loop (read and send a message from the message queue). Another is a non-queue message, he is bypassed the system queue and message queue, and the message is sent directly to the window process. For example, when a user activates a window system to send WM_Activate, WM_SETFOCUS, and WM_SETCURSOR. Send a WM_CREATE message when you create a window. Behind you will see that MS is very reasonable, as well as his entire implementation mechanism.
Here, the Message cycle of the MFC is described here. First look at the program startup, how to enter the message loop:
_twinmain -> AFXWINMAIN -> AFXWININIT -> CWINTHREAD :: InitApplication -> Cwinthread :: InitInstance -> CWINTHREAD :: Run
The message loop of the non-dialog box program starts from a RUN of this cwinthread ...
Part 1: Message loop mechanism for non-dialog box program.
//thrdcore.cpp//main running routine unstert_valid (this);
// for tracking the iDLE TIME STATEBOOL BIDLE = TRUE; Long LidleCount = 0;
// acquire and dispatch messages until a WM_QUIT message is received.for (;;) {// phase1:! Check to see if we can do idle work while (bIdle && :: PeekMessage (& m_msgCur, NULL, NULL, NULL, PM_NOREMOVE )) {// call onidle while in bidle statiff (! 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; lIdleCount = 0;}} while (:: PeekMessage (& m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); } // Unlimited loop, the exit condition is to receive a WM_Quit message.
Assert (false); // not reachable}
This is an infinite loop, his exit condition is to receive a WM_Quit message:
IF (! pumpMessage ()) Return ExitInstance ();
In PumpMessage, if you receive a WM_QUIT message, return false, so the exitInstance () function executes, jump out of the loop, return the exit code of the program. So, a program is to exit, only use the function in the code.
Void PostquitMessage (Int nexitcode). Specifies that the quit code Nexitcode can exit the program.
Let's discuss the process of this function Run, divided into two steps:
1, the first internal cycle PHASE1. The BIdle represents whether the program is idle. What he means is that if the program is idle and there is no message to be processed in the message queue, then the virtual function OnIdle is invalid. In this process, the UI interface (such as the Enable and Disable status of the toolbar button), delete temporary objects (such as object pointers you get with fromhandle. For this reason, the object pointer to the from "to send by fromHandle is not secure between the functions. Because he has no persistence). OnIdle is overloaded, you can overload him and return True to keep the message loop in idle state.
Note: MS uses temporary objects for efficiency, making memory efficiently, and automatically revokes resources when idle. Regarding the translation of the handle into an object, there may be several methods. Generally, an object OBJ is applied first, then use Obj.attatch to bind a handle. This generated object is permanent, you must use obj.detach to release the object.
2, the second internal cycle Phase2. Start the message pump (PumpMessage) in this loop, if not a WM_QUIT message, the message pump sends a message to (:: DispatchMessage). The destination of the message is the window corresponding to the HWND field in the message structure.
//thrdcore.cppbool cwinthread :: pumpMessage () {assert_valid (this); // If it is wm_quit to exit the function (Return False), this will cause the program to end. IF (! :: getMessage (& M_MSGCur, Null, Null, 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 returnaf false;}
#ifdef _DEBUGif (m_nDisablePumpCount = 0!) {TRACE0 ( "Error: CWinThread :: PumpMessage called when not permitted./n"); ASSERT (FALSE);} # endif # ifdef _DEBUGif (afxTraceFlags & traceAppMsg) _AfxTraceMsg (_T ( " PumpMessage "), & m_msgcur); # endif
// Process this message
IF (M_Msgcur.Message! = WM_KICKIDLE &&! PretranslateMessage (& M_MSGCUR))
{:: TranslateMessage; // Key Conversion :: DispatchMessage; // Delivery message} Return True;
There is a particularly important function in this step. Everyone must know: PretranslateMessage. This function performs pre-processing of the message before :: DispatchMessage sends a message to the window. The PretranslateMessage function is a member function of CWINTHREAD. When you overload, you are in the View class or the main window class, then how did it enter other classes? code show as below:
//thrdcore.cppbool cwinthread :: PretranslateMessage (msg * pmsg) {ask_VALID (this);
// If it is a thread message, the processing function IF of the thread message will be called (PMSG-> hwndMsSageex (PMSG)) Return True;
// Walk from target to main windowcwnd * PMainWnd = AFXGETMAINWND (); if (cwnd :: walkpretranslatetree (pmainwnd-> getsafehwnd (), pmsg) Return True;
// in case of modeless dialogs, Last Chance Route Through Main // Window's Accelerator Tableif (PMainWnd! = NULL) {CWND * PWND = CWnd :: fromHandle (PMSG-> HWND); if (PWND-> getTopyLEVET ()! = PMainWnd) Return PMainWnd-> PretranslateMessage (PMSG);
Return False; // no special processing}
It can be seen from this function:
First, if (PMSG-> hWnd == Null), this is a thread message. Call CWINTHREAD :: DispatchThreadMessageex to message mapping table Find Message Inlet and call message processing functions.
Note: Generally, the message between the thread is generally transmitted. He and the window message are different. You need to specify the thread ID. The message is placed in the message queue to the target thread; uses on_thread_message (message, memberfxn) macro to map thread messages And his handler. This macro must be in the application class (inherited from cwinthread) because only the application class handles thread messages. If you use this macro in other classes (such as view classes), the message processing function of thread messages will not be able to get a thread message. Second, the PretranslateMessage function of the target window of the message first gets the message processing, if the function returns false, then his parent window will get the handling of the message until the main window; if the function returns true (indicates that the message has been processed), Then do not need to call the PretranslateMessage function of the parent class. In this way, the target window of the message and his parent window can have the opportunity to call PretranslateMessage - Preprocessing before the message is sent to the window (if you handle it, return false ", if you want a message If you don't pass it to the parent class, you will return true.
Third, if the message target window and the main window do not have a parent-child relationship, then call the PretranslateMessage function of the main window. why is it like this? By the second step, the parent window of a window is not a main window, although its PretranslateMessage returns false, the main window does not have the opportunity to call the PretranslateMessage function. We know that the acceleration key is generally in the PretranslateMessage function of the frame window. I look for the processing of the accelerator key conversion in the MFC, only CFRAMEWND, CMDIFRAMEWND, CMDICHILDWND, etc. have. So, the third step means that if the target window (his parent window is not a main window, such as such a non-mode dialog box) makes the message's pre-processing continues to roam (his PretranslateMessage returns false), then give One chance to call PretranslateMessage for the main window (What is a certain acceleration message?), Which ensures that the main window can be made good if there is a non-mode dialog box.