Before entering the MFC Forum (detailed analysis of the window Program)

Source: Internet
Author: User

Winmain of MFC

Using MFC programmingProgramAt the beginning, the clerk raised the following question: where did my program start? The answer is: the execution starts from winmain. This problem is raised because the winmain () function is not visible in the MFC application they wrote. This function is hidden in the MFC framework, and the designers of MFC make it very common (mainly thanks to the message-driven programming mechanism of window, making it a general winmain () therefore, you do not need to changeCodeThe MFC designers do not advocate programmers to modify winmain () code. In MFC, the code that actually implements winmain () is the afxwinmain () function (according to its prefix afx, it is known that this is a global MFC function ).

A Win32 application (or process) is composed of one or more concurrent threads. The first thread to be started is called the main thread. In the window, generally, a thread is divided into two categories: interface thread and working thread. A working thread is a common thread. It has no window, no message queue, etc. The interface thread has one or more windows, has a message queue and other elements dedicated to the interface thread. Before discussing afxwinmain (), we should first briefly mention two important classes in MFC: cwinthread and cwinapp. cwinthread are classes used to encapsulate interface threads, and cwinapp is derived from cwinthread. In cwinthread, there are two important virtual functions initinstance () and exitinistance (). The MFC programmer should be familiar with these two functions. In cwinapp, another virtual function initapplication () is added. The main purpose of afxwinmain () is to see how these functions are called.

The afxwinmain () code is as follows:

Int afxapi afxwinmain (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 code above, afxgetthread () returns a pointer to the current interface thread object, and afxgetapp () returns a pointer to the application object if the application (or process) only one interface thread is running, and both return a global application object pointer, this global application object is the default theapp object of the MFC Application Framework (every time Appwizard is used to generate an SDI or MDI application, Appwizard will add the cyourapp theapp statement, afxgetapp () returns the theapp address ).

Cwinapp: initapplication (), cwinthread: initinstance (), cwinthread: exitinstance () is called. From the code above, I will not go into details. Next we will focus on cwinthread: Run.

Control center of MFC-cwinthread: Run ()

Cwinthread: Run () is the control center of MFC, and it is not exaggerated at all. In MFC, all messages from message queues are distributed in the cwinthread: Run () function, just like afxwinmain, this function is invisible to programmers, and its principle is the same as that of afxwinmain.

The first thing to mention is that for each message obtained from the message queue, MFC distributes messages according to the Message Type and a specific mode, this distribution mode is defined by MFC itself. A fixed message distribution process and a virtual function in this process that can dynamically change its behavior constitute the message distribution mode of MFC. Applications can customize their own message distribution modes by reloading these virtual functions. Through these virtual functions, MFC provides sufficient flexibility for applications. All the code discussed below comes fromSource codeThreadcore. cpp files in, all of which are members of cwinthread.

Cwinthread: Run () Structure

The code for cwinthread: Run () is as follows:

Code

    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 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;
Lidlecount = 0 ;
}
} While (::Peekmessage( & M_msgcur, null, pm_noremove ));
}
Assert (false ); // Not reachable
}

The process of cwinthread: Run () is as follows:

Determine whether the current thread is in idle state based on the idle flag and whether the message queue is empty (the meaning of this idle state is different from that of the operating system, is the so-called "idle" by MFC itself). If yes, call cwinthread: onidle (), which is also a virtual function that we are familiar.

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

Here, we found that MFC does not call getmessage () to retrieve messages from the thread message queue, but calls peekmessage (). The reason is that getmessage () is a function with synchronous behavior. If there is no message in the message queue, getmessage () will be blocked all the time, making the thread sleep, the operating system will wake up this thread until one or more messages exist in the message queue, and getmessage () will return. If the thread is in sleep state, it will not make the thread have the so-called "idle" state of MFC; while peekmessage () is a function with asynchronous behavior. If there is no message in the message queue, it will immediately return 0, does not cause the thread to be sleep.

In the above Code, there are two functions worth exploring. One is the idle processing function onidle (), and the other is the message distribution processing function pumpmessage (). Do not ignore the onidle () function of cwinthread. It does a lot of meaningful things. The following sections describe pumpmessage () and onidle () in the following sections.

Cwinthread: Run () Core-cwinthread: pumpmessage ()

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

Bool cwinthread: pumpmessage () {assert_valid (this); If (! : Getmessage (& m_msgcur, null) return false; // process this message if (m_msgcur.message! = Wm_kickidle &&! Pretranslatemessage (& m_msgcur) {: translatemessage (& m_msgcur);: dispatchmessage (& m_msgcur);} return true ;}

First, pumpmessage () calls getmessage () to retrieve a message from the message queue. Because pumpmessage () is called only when there is a message in the message queue, getmessage () will return immediately. Based on the returned value, determine whether the retrieved message is a wm_quit message (this message is generally put into the thread message queue by calling postquitmessage (). If yes, if false is returned, cwinthread: Run () is exited. cwinthread: Run () directly calls cwinthread: exitinstance () to exit the application. Getmessage () is followed by the familiar translatemessage () and dispatchmessage () functions.

It can be seen that whether to call translatemessage () and dispatchmessage () is determined by the return value of a function named pretranslatemessage (). If this function returns true, the message is not distributed to the window function for processing.

In my personal opinion, it is with this pretranslatemessage () that MFC can flexibly control the message distribution mode. It can be said that pretranslatemessage () is the message distribution mode of MFC.

<3> special feature of MFC-pretranslatemessage ()

After layers of skin, I finally found the most distinctive cwinthread: Run (), which is the pretranslatemessage () function. It is displayed as "Hello, world!" In the SDK !" The difference in the message loop of the program is that, when MFC has this pretranslatemessage (), pretranslatemessage () gets the message processing right of the application first! Next we will perform a skin-peeling Analysis on pretranslatemessage. As before, let's first look at the actual pretranslatemessage () code:

Bool cwinthread: pretranslatemessage (MSG * PMSG)

{

Assert_valid (this );

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

If (PMSG-> hwnd = NULL & dispatchthreadmessageex (PMSG) return true;

// Walk from target to Main Window

Cwnd * pmainwnd = afxgetmainwnd ();

If (cwnd: descripretranslatetree (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 pretranslatemessage () process is as follows:

First, determine whether the message is a thread message (the message's window handle is empty). If yes, submit it to dispatchthreadmessageex () for processing. Dispatchthreadmessageex () is not the focus of our discussion.

Call cwnd: walkpretranslatetree () to process the message. Note that a parameter of this function is the handle of the Main Window of the thread. This is the core code of pretranslatemessage, this function will be analyzed in detail later.

This is special and extra for non-mode dialog boxes.

The following describes the cwnd: javaspretranslatetree () function in detail. Its code is very simple:

Bool Pascal cwnd: FIG (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 = 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: Define pretranslatetree () is very simple. The window that owns the message first obtains the message processing permission, if it does not want to process the message (the pretranslatemessage () function of the window object returns false), it will give the processing permission to its parent window, so as to traverse to the root of the tree, until hwndstop (in cwinthread: pretranslatemessage (), hwndstop indicates the handle of the main thread window) is encountered ). Remember the transfer direction of this message processing right. It is transmitted to the root of the tree by a common node or leaf node of the tree!

Summary:

Next, let's make a summary of this chapter.

The most distinctive feature of the message control flow of MFC is the virtual function pretranslatemessage () of the cwnd class. By reloading this function, we can change the message control flow of MFC, you can even create a new control flow. The following chapter describes the implementation of MFC in detail.

Only messages that pass through the message queue are affected by pretranslatemessage (). messages sent directly to the window in sendmessage () or other similar ways without passing through the Message Queue do not ignore pretranslatemessage () exist

A message sent to pretranslatemessage () is an untranslated message that has not been processed by translatemessage (). In some cases, it must be processed carefully to avoid missing messages.

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.