4. MFC threads, message queues, and MFCProgram"Life and Death"
The analysis of the main thread startup and Message Queue processing process of the MFC program will help us to further understand the relationship between the UI thread and message queue, for this reason, we need to briefly describe the "Life and Death" of the MFC Program (Hou Jie: "a simple introduction to MFC").
Use the wizard of VC ++ 6.0 to complete the mfcthread of the most simple single-document architecture MFC application:
(1) input the mfc exe project name mfcthread;
(2) Select a single-document architecture. The document/view structure is not supported;
(3) Select none for ActiveX, 3D iner, and other options.
Let's analyze this project. Below are the coreSource code: Mfcthread. h file
Class cmfcthreadapp: Public cwinapp { Public: Cmfcthreadapp ();// Overrides // Classwizard generated virtual function overrides // {Afx_virtual (cmfcthreadapp) Public: Virtual bool initinstance (); //} Afx_virtual // Implementation Public: // {Afx_msg (cmfcthreadapp) Afx_msg void onappabout (); // Note-The classwizard will add and remove member functions here. // Do not edit what you see in these blocks of generated code! //} Afx_msg Declare_message_map () }; |
Mfcthread. cpp File
Cmfcthreadapp theapp; //////////////////////////////////////// ///////////////////////////////////// // Cmfcthreadapp Initialization Bool cmfcthreadapp: initinstance () { ... Cmainframe * pframe = new cmainframe; M_pmainwnd = pframe; // Create and load the frame with its resources Pframe-> loadframe (idr_mainframe, ws_overlappedwindow | fws_addtotitle, null, null ); // The one and only window has been initialized, so show and update it. Pframe-> showwindow (sw_show ); Pframe-> updatewindow (); Return true; } |
Mainfrm. h file
# Include "childview. H" Class cmainframe: Public cframewnd { Public: Cmainframe (); Protected: Declare_dynamic (cmainframe) // Attributes Public: // Operations Public: // Overrides // Classwizard generated virtual function overrides // {Afx_virtual (cmainframe) Virtual bool precreatewindow (createstruct & CS ); Virtual bool on1_msg (uint NID, int ncode, void * pextra, afx_cmdhandlerinfo * phandlerinfo ); //} Afx_virtual // Implementation Public: Virtual ~ Cmainframe (); # Ifdef _ debug Virtual void assertvalid () const; Virtual void dump (cdumpcontext & DC) const; # Endif Cchildview m_wndview; // Generated message map Functions Protected: // {Afx_msg (cmainframe) Afx_msg void onsetfocus (cwnd * poldwnd ); // Note-The classwizard will add and remove member functions here. // Do not edit what you see in these blocks of generated code! //} Afx_msg Declare_message_map () }; |
Mainfrm. cpp File
Implement_dynamic (cmainframe, cframewnd) Begin_message_map (cmainframe, cframewnd) // {Afx_msg_map (cmainframe) // Note-The classwizard will add and remove mapping macros here. // Do not edit what you see in these blocks of generated code! On_wm_setfocus () //} Afx_msg_map End_message_map () //////////////////////////////////////// ///////////////////////////////////// // Cmainframe construction/destruction Cmainframe: cmainframe () { // Todo: Add member initialization code here } Cmainframe ::~ Cmainframe () {} Bool cmainframe: precreatewindow (createstruct & CS) { If (! Cframewnd: precreatewindow (CS )) Return false; // Todo: Modify the window class or styles here by modifying // The createstruct CS CS. dwexstyle & = ~ Ws_ex_clientedge; CS. lpszclass = afxregisterwndclass (0 ); Return true; } |
Childview. h file
// Cchildview window Class cchildview: Public cwnd { // Construction Public: Cchildview (); // Attributes Public: // Operations Public: // Overrides // Classwizard generated virtual function overrides // {Afx_virtual (cchildview) Protected: Virtual bool precreatewindow (createstruct & CS ); //} Afx_virtual // Implementation Public: Virtual ~ Cchildview (); // Generated message map Functions Protected: // {Afx_msg (cchildview) Afx_msg void onpaint (); //} Afx_msg Declare_message_map () }; Childview. cpp File // Cchildview Cchildview: cchildview () {} Cchildview ::~ Cchildview () {} Begin_message_map (cchildview, cwnd) // {Afx_msg_map (cchildview) On_wm_paint () //} Afx_msg_map End_message_map () //////////////////////////////////////// ///////////////////////////////////// // Cchildview message handlers Bool cchildview: precreatewindow (createstruct & CS) { If (! Cwnd: precreatewindow (CS )) Return false; CS. dwexstyle | = ws_ex_clientedge; CS. Style & = ~ Ws_border; CS. lpszclass = afxregisterwndclass (cs_hredraw | cs_vredraw | cs_dblclks,: loadcursor (null, idc_arrow ), Hbrush (color_window + 1), null ); Return true; } Void cchildview: onpaint () { Cpaintdc DC (this); // device context for painting // Todo: add your message handler code here // Do not call cwnd: onpaint () for painting messages } |
File mfcthread. H and mfcthread. the classes defined and implemented by CPP, cmfcthreadapp, inherit from the cwinapp class, And the cwinapp class inherits from the cwinthread class (the cwinthread class inherits from the cwintarget class). Therefore, cmfcthread is essentially an MFC Thread class, the related class hierarchy is provided:
We extract part of the cwinapp class prototype:
Class cwinapp: Public cwinthread { Declare_dynamic (cwinapp) Public: // Constructor Cwinapp (lpctstr lpszappname = NULL); // default app name // Attributes // Startup ARGs (do not change) Hinstance m_hinstance; Hinstance m_hprevinstance; Lptstr m_lpcmdline; Int m_ncmdshow; // Running ARGs (can be changed in initinstance) Lpctstr m_pszappname; // human readable name Lpctstr m_pszexename; // executable name (no spaces) Lpctstr m_pszhelpfilepath; // default based on module path Lpctstr m_pszprofilename; // default based on app name // Overridables Virtual bool initapplication (); Virtual bool initinstance (); Virtual int exitinstance (); // return app exit code Virtual int run (); Virtual bool onidle (long lcount); // return true if more idle Processing Virtual lresult processwndprocexception (cexception * E, const MSG * PMSG ); Public: Virtual ~ Cwinapp (); Protected: Declare_message_map () }; |
The winmain of the SDK program is now completed by three functions of cwinapp:
Virtual bool initapplication (); Virtual bool initinstance (); Virtual int run (); |
"Cmfcthreadapp theapp;" The global variable theapp defined by the statement is the application object of the entire program. Each MFC application has one. When we execute the mfcthread program, this global variable is constructed. After theapp is configured, run winmain. However, the program does not contain winmainCodeWhere is it? Originally, MFC was ready and directly added to the application code by linker. Its prototype is (stored in the appmodul. cpp file provided in the installation directory of VC ++ 6.0 ):
Extern "C" int winapi _ Twinmain (hinstance, hinstance hprevinstance, Lptstr lpcmdline, int ncmdshow) { // Call shared/exported winmain Return afxwinmain (hinstance, hprevinstance, lpcmdline, ncmdshow ); } |
The called afxwinmain is as follows (in the winmain. cpp file provided in the installation directory of VC ++ 6.0 ):
Int afxapi afxwinmain (hinstance, hinstance hprevinstance, Lptstr lpcmdline, int ncmdshow) { Assert (hprevinstance = 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: # Ifdef _ debug // Check for missing afxlocktempmap CILS If (afxgetmodulethreadstate ()-> m_ntempmaplock! = 0) { Trace1 ("Warning: temp map lock count non-zero (% LD). \ n ", Afxgetmodulethreadstate ()-> m_ntempmaplock ); } Afxlocktempmaps (); Afxunlocktempmaps (-1 ); # Endif Afxwinterm (); Return nreturncode; } |
We extract the trunk. In fact, the main tasks of this function are:
Cwinthread * pthread = afxgetthread (); Cwinapp * PAPP = afxgetapp (); Afxwininit (hinstance, hprevinstance, lpcmdline, ncmdshow) PAPP-> initapplication () Pthread-> initinstance () Pthread-> Run (); |
Initapplication is the place where the window type is registered, initinstance is the place where the window is generated and displayed, and run is the place where messages are extracted and distributed. In this way, MFC corresponds to the Win32 SDK program. Cwinthread: run is the "Active Water Source" of program life (Hou Jie: "Let's get down to MFC", and the function exists in the thrdcore. cpp file provided in the installation directory of VC ++ 6.0 ):
// 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 )); } Assert (false); // not reachable } |
The pumpmessage function corresponds:
//////////////////////////////////////// ///////////////////////////////////// // Cwinthread implementation helpersBool 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; } |
Therefore, ignore the idle status, and the entire run execution extraction trunk is:
Do { : Getmessage (& MSG ,...); Pretranslatemessage {& MSG ); : Translatemessage (& MSG ); : Dispatchmessage (& MSG ); ... } While (: peekmessage (...)); |
As a result, we have established a correspondence between the MFC message acquisition and derivation mechanism and the Win32 SDK program. Next, we will continue to analyze the "Bypass" Process of the MFC message.
In MFC, any windows message can be blocked as long as it is a cwnd derivative category. If you want to process messages in a Windows-independent MFC category (such as cdocument and cwinapp), you must derive the message from c1_target and only receive the wm_command message. All the classes that can perform message_map are inherited from c1_target, for example:
The definition of message_map in MFC depends on the following three macros:
Declare_message_map () Begin_message_map ( Theclass, // specifies the name of the class whose message map this is Baseclass // specifies the name of the base class of theclass ) End_message_map () |
Our program involves mfcthread. H, mainfrm. H, and childview. h.
Declare_message_map () Mfcthread. cpp File Begin_message_map (cmfcthreadapp, cwinapp) // {Afx_msg_map (cmfcthreadapp) On_command (id_app_about, onappabout) // Note-The classwizard will add and remove mapping macros here. // Do not edit what you see in these blocks of generated code! //} Afx_msg_map End_message_map () Mainfrm. cpp File Begin_message_map (cmainframe, cframewnd) // {Afx_msg_map (cmainframe) // Note-The classwizard will add and remove mapping macros here. // Do not edit what you see in these blocks of generated code! On_wm_setfocus () //} Afx_msg_map End_message_map () Childview. cpp File Begin_message_map (cchildview, cwnd) // {Afx_msg_map (cchildview) On_wm_paint () //} Afx_msg_map End_message_map () |
With these macros, MFC creates a message ing table (Message Flow Network) that matches the corresponding message processing function according to the Message Flow Network to complete the "Bypass" of the entire message ".
I believe you have such a question: the program defines theapp global variables of the cwinapp class, but it never calls afxbeginthread or theapp. createthread: How does the thread corresponding to theapp start?
A: MFC has used a brilliant trick here. In fact, the program starts to run. The first thread is started by the operating system (OS). In the cwinapp constructor, MFC directed theapp "" to this thread, the specific implementation is as follows:
Cwinapp: cwinapp (lpctstr lpszappname) { If (lpszappname! = NULL) M_pszappname = _ tcsdup (lpszappname ); Else M_pszappname = NULL; // Initialize cwinthread state Afx_module_state * pmodulestate = _ afx_cmdtarget_getstate (); Afx_module_thread_state * pthreadstate = pmodulestate-> m_thread; Assert (afxgetthread () = NULL ); Pthreadstate-> m_pcurrentwinthread = this; Assert (afxgetthread () = This ); M_hthread =: getcurrentthread (); M_nthreadid =: getcurrentthreadid (); // Initialize cwinapp state Assert (afxcurrentwinapp = NULL); // only one cwinapp object please Pmodulestate-> m_pcurrentwinapp = this; Assert (afxgetapp () = This ); // In non-running state until winmain M_hinstance = NULL; M_pszhelpfilepath = NULL; M_pszprofilename = NULL; M_pszregistrykey = NULL; M_pszexename = NULL; M_precentfilelist = NULL; M_pdocmanager = NULL; M_atomapp = m_atomsystemtopic = NULL; // Microsoft lazy? Or he thinks // Is the definition of such a connection clearer? M_lpcmdline = NULL; M_pcmdinfo = NULL; // Initialize wait cursor state M_nwaitcursorcount = 0; M_hcurwaitcursorrestore = NULL; // Initialize current printer state M_hdevmode = NULL; M_hdevnames = NULL; M_nnumpreviewpages = 0; // not specified (defaults to 1) // Initialize Dao state M_lpfndaoterm = NULL; // will be set if afxdaoinit called // Other Initialization M_bhelpmode = false; M_nsaid fetypoolsize = 512; // default size } |
Obviously, theapp member variables are assigned to the value related to the current thread started by the OS, such as the Code:
M_hthread =: getcurrentthread (); // The thread handle of theapp is equal to the current thread handle M_nthreadid =: getcurrentthreadid (); // The thread ID of theapp is equal to the current thread ID |
Therefore, the cwinapp class is almost customized for the first thread of the MFC program. It cannot be started again by afxbeginthread or theapp. createthread. This is the meaning of the cwinapp class and theapp global variables! If you want to add another UI thread, do not inherit the class cwinapp, but inherit the class cwinthread. For more information, see section 1st. Since we generally process messages in all windows using the main thread (which is actually the first thread started by the OS in the MFC Program, so we have almost no need to start the UI thread again!