In-depth introduction to Win32 Multi-thread MFC (2)

Source: Internet
Author: User

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 helpers

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;
}

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!

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.