&MFC message routing __mfc for MFC message loops

Source: Internet
Author: User
Tags assert getmessage goto message queue win32
It should be clear that MFC's message loops (:: GetMessage,::P eekmessage), Message pumps (CWinThread::P umpmessage) and MFC messages are routed between windows in two different things. In an MFC application (the application class is based on CWinThread inheritance), you must have a message loop that reads the message from the application's message queue and sends it out (:D ispatchmessage). and message routing refers to the message sent out after the system (USER32. DLL) to which window the message is posted, and how the message is passed between windows at a later time.
Messages are grouped into queue messages (message queues that enter threads) and non-queued messages (message queues that do not enter threads). For queue messages, the most common are messages triggered by the mouse and keyboard, such as Wm_mousermove,wm_char, and for example: WM_PAINT, Wm_timer, and Wm_quit. When the mouse and keyboard events are triggered, the corresponding mouse or keyboard driver converts these events to the corresponding message, and then to the system message queue, where the Windows system is responsible for adding the message to the corresponding thread's message queue, so there is a message loop (read and dispatch messages from the message queue). There is also a non-queue message that bypasses system queues and message queues and sends messages directly to the window process. For example, when a user activates a window system to send Wm_activate, Wm_setfocus, and Wm_setcursor. Sends a WM_CREATE message when the window is created. In the back you will see that MS is so designed to make sense, as well as his complete set of implementation mechanisms. Programmers using MFC are beginning to ask the question: Where did my program start. The answer is: Start from WinMain (). The problem was raised because the WinMain () function was not visible in the MFC application they wrote. This function is hidden in the MFC framework, MFC's designers make it very common (this is mainly due to the window's message-driven programming mechanism, making a common WinMain () very easy), so in general, do not need to change the WinMain () code, MFC's designers do not encourage programmers to modify WinMain () code. In MFC, the code that actually implements WinMain () is the Afxwinmain () function (which, according to its prefix AFX, knows that this is a global MFC function).

A WIN32 application (or process) is made up of one or more concurrent threads. The first thread that is started is called the mainline, under window, the thread is generally divided into two categories, the interface thread and the worker thread, the worker thread is the general thread, it has no window, no message queue, etc. An interface thread has one or more windows, and has a message queue and other elements that are specifically part of the interface thread. Before discussing Afxwinmain (), let's briefly mention two important classes in MFC, CWinThread and Cwinapp,cwinthread are classes that encapsulate interface threads, and CWinApp derive from CWinThread. In CWinThread, there are two very important virtual functions InitInstance () and Exitinistance (), and MFC programmers should be familiar with these two functions. In CWinApp, another virtual function initapplication () is added, and the main purpose of Afxwinmain () is to see how these functions are invoked.

The code for Afxwinmain () is as follows:

int Afxapi Afxwinmain (hinstance hinstance, HInstance hprevinstance,

LPTSTR lpcmdline, int ncmdshow)

{

ASSERT (hprevinstance = NULL); file://under Win32, hPrevInstance is always null

int nreturncode =-1;

cwinthread* pThread = Afxgetthread ();

cwinapp* papp = AfxGetApp ();

AFX Internal Initialization

if (! AfxWinInit (HInstance, hPrevInstance, lpCmdLine, nCmdShow))

Goto Initfailure;

APP global initializations (RARE)

if (Papp!= NULL &&!papp->initapplication ())

Goto Initfailure;

Perform specific initializations

if (!pthread->initinstance ())

{

if (Pthread->m_pmainwnd!= NULL)

{

TRACE0 ("warning:destroying non-null m_pmainwnd\n");

Pthread->m_pmainwnd->destroywindow ();

}

Nreturncode = Pthread->exitinstance ();

Goto Initfailure;

}

Nreturncode = Pthread->run ();

Initfailure:

Afxwinterm ();

return nreturncode;

}

In the above code, Afxgetthread () returns a pointer to the current interface thread object, and AfxGetApp () returns a pointer to the Application object, which returns a global Application object pointer if the application (or process) has only one interface thread running. , this global Application object is the default Theapp object for the MFC application Framework (AppWizard adds CYourApp Theapp This statement each time an SDI or MDI application is generated using AppWizard, AfxGetApp () The address of this theapp is returned.

Cwinapp::initapplication (), Cwinthread::initinstance (), Cwinthread::exitinstance () is called, from the above code to see, I will not repeat. Here we focus on the Cwinthread::run ().

MFC's control center ――cwinthread::run ()

Said Cwinthread::run () is MFC's control center, not at all exaggerated. In MFC, all messages from Message Queuing are dispatched in the Cwinthread::run () function, and, like Afxwinmain (), this function is not visible to programmers, as is the case with Afxwinmain ().

The first thing to mention is that each message from the message queue, MFC based on the type of message, according to a specific pattern of distribution processing, the distribution pattern is MFC's own definition. The fixed message distribution process and the virtual functions that dynamically change their behavior in this process form the MFC message distribution model. Applications can override these virtual functions to customize their own message distribution mode locally. It is through these virtual functions that MFC gives the application enough flexibility. All of the code discussed below comes from the Threadcore.cpp files in the MFC source code, all of which are members of the CWinThread.

Structure of the Cwinthread::run ()

The code for Cwinthread::run () is as follows:

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 received.

for (;;)

{

Phase1:check to = if we can do idle work

while (Bidle &&

!::P eekmessage (&m_msgcur, NULL, NULL, 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;

Lidlecount = 0;

}

while (::P eekmessage (&m_msgcur, NULL, NULL, NULL, pm_noremove));

}

ASSERT (FALSE); Not reachable

}

The process of Cwinthread::run () is as follows:

To determine whether the current thread is idle based on the idle flag and whether the message queue is empty (the meaning of this "idle" is different from the meaning of the operating system, is MFC's own so-called "idle"), and if so, call Cwinthread::onidle (). This is also a virtual function that we are familiar with.

If not, remove the message from the message queue and process it until the message queue is empty.

Here, we find that MFC does not call GetMessage () to fetch messages from thread message queues, but instead calls PeekMessage (). The reason for this is that GetMessage () is a function with synchronization behavior, and if there is no message in the message queue, GetMessage () is blocked, causing the thread to sleep until there is one or more messages in the message queue, and the operating system wakes the thread, GetMessage () is returned, if the thread is asleep, the thread will not have MFC's so-called "idle" state, while PeekMessage () is a function with asynchronous behavior, if there is no message in the message queue, it returns 0 immediately, does not cause the thread to be in a sleep state.

In the above code, there are two functions worth exploring, one is the idle processing function OnIdle () and the other is the message distribution handler pumpmessage (). Don't ignore the CWinThread OnIdle () function, it does a lot of meaningful things. The following discussion pumpmessage (), OnIdle () will be discussed in later chapters.

Core ――cwinthread of Cwinthread::run ()::P umpmessage ()

The title emphasizes the importance of pumpmessage (), run () is the control center of MFC, and PumpMessage () is the core of run (), so from the real control center of MFC is PumpMessage (). The code for PumpMessage () is extremely simple:

BOOL CWinThread::P umpmessage ()

{

Assert_valid (this);

if (!::getmessage (&m_msgcur, NULL, NULL, NULL))

return FALSE;

Process this message

if (m_msgcur.message!= wm_kickidle &&!) PreTranslateMessage (&m_msgcur))

{

:: TranslateMessage (&m_msgcur);

::D ispatchmessage (&m_msgcur);

}

return TRUE;

}

First, PumpMessage () invokes GetMessage () to fetch a message from the message queue, and because PumpMessage () is invoked when there is a message in the message queue, GetMessage () returns immediately, based on its return value, To determine whether the currently fetched message is not a WM_QUIT message (this message is generally used to put the thread message queue by calling PostQuitMessage), and if so, return to False,cwinthread::run (), CWinThread:: Run () directly calls Cwinthread::exitinstance () to exit the application. Behind GetMessage () are the TranslateMessage () and DispatchMessage () functions that we are familiar with.

As you can see, whether the call to TranslateMessage () and DispatchMessage () is determined by a return value called the PreTranslateMessage () function, if the function returns True, The message is not distributed to window function processing.

In my opinion, it is with this pretranslatemessage (), so that MFC can flexibly control the distribution mode of messages, you can say, PreTranslateMessage () is the MFC message distribution mode.

< three &GT;MFC features ――pretranslatemessage ()

After layers of Skinner, finally found the Cwinthread::run () the most distinctive place, this is the PreTranslateMessage () function. "Hello, world!", as previously written using the SDK The message loop of a program is different in that MFC has this pretranslatemessage (), and PreTranslateMessage () first gets the message processing power of the application. Below we carry on the skinning analysis to the PreTranslateMessage (). Like the previous face, first look at the actual pretranslatemessage () code:

BOOL CWinThread::P retranslatemessage (msg* pMsg)

{

Assert_valid (this);

If this is a thread-message, the short-circuit this function

if (Pmsg->hwnd = = NULL && Dispatchthreadmessageex (PMSG)) return TRUE;

Walk from Target to main window

cwnd* pMainWnd = AfxGetMainWnd ();

if (Cwnd::walkpretranslatetree (Pmainwnd->getsafehwnd (), PMSG)) return TRUE;

In the case of modeless dialogs, 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 process of PreTranslateMessage () is as follows:

First, determine if the message is a thread message (the message's window handle is empty), and if so, give it to Dispatchthreadmessageex () for processing. We don't care about Dispatchthreadmessageex (), it's not the focus of our discussion.

Call Cwnd::walkpretranslatetree () to process the message, note that one of the parameters of the function is the handle to the main window of the thread, which is the core code of the PreTranslateMessage (), which is followed by a detailed analysis of the function.

For modeless dialogs, this is a special, extra processing.

The Cwnd::walkpretranslatetree () function is discussed in detail below, and its code is simple:

BOOL PASCAL Cwnd::walkpretranslatetree (HWND hwndstop, msg* pMsg)

{

ASSERT (hwndstop = NULL | |:: IsWindow (Hwndstop));

ASSERT (PMSG!= NULL);

Walk from the target window up to the Hwndstop window checking

If any window wants to translate this message

for (HWND hwnd = pmsg->hwnd; hwnd!= NULL; hwnd =:: GetParent (HWND))

{

cwnd* pwnd = cwnd::fromhandlepermanent (hWnd);

if (pwnd!= NULL)

{

Target window is a C + + window

if (Pwnd->pretranslatemessage (PMSG))

return TRUE; Trapped by Target window (eg:accelerators)

}

Got to hwndstop window without interest

if (hWnd = = hwndstop)

Break

}

return FALSE; No special processing

}

The policy used by Cwnd::walkpretranslatetree () is simple, and the window that owns the message first obtains the message's processing power if it does not want to process the message (the PreTranslateMessage () function of the Window object returns false) , the processing power is handed to its Father window, which traverses the root of the tree until it encounters Hwndstop (in CWinThread::P retranslatemessage (), Hwndstop represents the handle of the thread main window). Remember that this message processing power is passed by a general node of the tree or a leaf node to the root of the tree.

Summary:

A summary of this chapter is given below.

MFC Message control flow is the most distinctive place is the CWnd class of virtual Functions PreTranslateMessage (), by overloading this function, we can change the MFC message control process, or even a new control flow, in the following chapter will be the implementation of MFC detailed introduction.

Only messages passing through Message Queuing are affected by PreTranslateMessage (), messages that are sent directly to the window by SendMessage () or other similar methods do not ignore the presence of PreTranslateMessage ()

The message passed to PreTranslateMessage () is a message that has not been translated, it has not been processed by TranslateMessage (), and in some cases it has to be handled carefully so as not to omit the message.

Http://blog.sina.com.cn/s/blog_4bc8d5f301000a1j.html


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.