The principle of the message loop, think that can solve all the similar problems ... But take a look at the following codeDWORD Ctestmfcdlg::threadfunc (PVOID yy)
{
CAboutDlg Dlg;
Dlg. DoModal ();
return 0;
}
void Ctestmfcdlg::onok ()
{
:: CreateThread (Null,null, (lpthread_start_routine) threadfunc,null,null,null);
}
The following assert appears on the compile run on vc++6.0.
void Cwnd::assertvalid () const
{
......
chandlemap* PMap = Afxmaphwnd ();
......
cobject* p;
ASSERT (p = pmap->lookuppermanent (m_hwnd))! = NULL | |
(P = pmap->lookuptemporary (m_hwnd)) = NULL);
ASSERT ((cwnd*) p = = this); Must be US
MFC has a global hash table (obtained via Afxmaphwnd ()), which is used to associate the HWND handle with the encapsulated object CWnd of MFC so that it can be cwnd::fromhandle () Such functions attach the CWnd object to an existing HWND handle, using MFC's encapsulation function to simplify the direct operation of the HWND. Obviously, the assert here is because the CWnd object finds the CWnd object pointer from the hash table according to its window handle (m_hwnd) is not the same as the object itself (this). This means that the hash table registered to the CWnd object when it was created is not the same as the hash table currently being retrieved. Why would it be like that?
chandlemap* PASCAL Afxmaphwnd (BOOL bcreate)
{
afx_module_thread_state* pState = Afxgetmodulethreadstate ();
if (Pstate->m_pmaphwnd = = NULL && bcreate)
{
.......
Pstate->m_pmaphwnd = new Chandlemap (Runtime_class (Ctempwnd), Offsetof (CWnd, m_hwnd));
......
}
Return pstate->m_pmaphwnd;
It seems that this hash table is related to Afxgetmodulethreadstate (), continue
afx_module_thread_state* AFXAPI afxgetmodulethreadstate ()
{
Return Afxgetmodulestate ()->m_thread. GetData ();
}
afx_module_state* AFXAPI afxgetmodulestate ()
{
_afx_thread_state* pState = _afxthreadstate;
Afx_module_state* PResult;
if (pstate->m_pmodulestate! = NULL)
{
Thread state's module state serves as override
PResult = pstate->m_pmodulestate;
}
Else
{
Otherwise, use Global app State
PResult = _afxbasemodulestate.getdata ();
}
ASSERT (PResult! = NULL);
return pResult;
}
So what is _afxthreadstate?
Extern_thread_local (_afx_thread_state, _afxthreadstate)
Class _afx_thread_state:public CNoTrackObject
{
Public
_afx_thread_state ();
Virtual ~_afx_thread_state ();
Override for M_pmodulestate in _afx_app_state
Afx_module_state* m_pmodulestate;
Afx_module_state* m_pprevmodulestate;
#define Thread_local (class_name, Ident_name)/
Afx_datadef cthreadlocal<class_name> Ident_name;
#define Extern_thread_local (class_name, Ident_name)/
extern afx_data thread_local (class_name, Ident_name)
The result of the analysis is the extern cthreadlocal<_afx_thread_state> _afxthreadstate.
Template<class type>
Class Cthreadlocal:public Cthreadlocalobject
{
Afx_inline type* GetData ()
{
type* PData = (type*) cthreadlocalobject::getdata (&createobject);
ASSERT (PData! = NULL);
return pData;
}
Afx_inline operator type* ()
{return GetData ();}
Afx_inline type* operator-> ()
{return GetData ();}
Static cnotrackobject* AFXAPI CreateObject ()
{return new TYPE;}
As you can see, _afxthreadstate is a global object. This object allows you to get the _afx_thread_state object, which is thread-dependent. Cthreadlocalobject's code is no longer parsed, presumably checking the current thread-private data, or, if there is one, creating a new object (that is, _afx_thread_state).
Continue to see Afxgetmodulestate (), roughly meaning to get the Afx_module_state object associated with the current line threads, or get the default Afx_module_state object for the process if not.
Process_local (_afx_base_module_state, _afxbasemodulestate)
Class _afx_base_module_state:public Afx_module_state
#define Process_local (class_name, Ident_name)/
Afx_datadef cprocesslocal<class_name> Ident_name;
#define Extern_process_local (class_name, Ident_name)/
extern afx_data process_local (class_name, Ident_name)
Continue to see Afxgetmodulethreadstate () and access its M_thread members after the Afx_module_state object has been obtained. This is another thread-related data that can get afx_module_state private data in different threads.
Afx_module_state (global data for a MODULE)
Class Afx_module_state:public CNoTrackObject
{
Define thread local portions of module state
Thread_local (Afx_module_thread_state, M_thread)
Now simply summarize, afxgetmodulestate () can get the afx_module_state associated with the execution thread, and afxgetmodulethreadstate () can get the afx_module_ associated with the execution thread The state of the afx_module_thread_state associated with the current execution thread. It looks a little bit around, just a few more words. We can understand that each thread executes with a module as the default context, such as when looking for a resource, which is searched by default from the module. This is why we need to switch resources at the output function of the DLL, in fact the context of the caller thread is set to the module where the current code is located, thus guaranteeing the proper loading of the resource. In addition, the code in the module needs to hold different global data based on different execution threads (not to avoid conflicting access.) ), the module can also have its own unique thread-related data. Therefore, afx_module_state can be used in different threads, and afx_module_thread_state can only be used in certain threads.
Now look again at Afxmaphwnd (), which returns a hash table with the module associated with the worker thread under the worker thread. We can now imagine that the hash table is completely different if the worker threads are associated with a different module, or under the same associated module, rather than the working thread.
Now continue to check the stack and discover that it is the message loop that asserts from CWnd
int Cwnd::runmodalloop (DWORD dwFlags)
{
......
if (! Afxgetthread ()->pumpmessage ())
cwinthread* AFXAPI Afxgetthread ()
{
Check for current thread in module thread state
afx_module_thread_state* pState = Afxgetmodulethreadstate ();
cwinthread* PThread = pstate->m_pcurrentwinthread;
If no CWinThread for the module and then use the global app
if (PThread = = NULL)
PThread = AfxGetApp ();
return pThread;
_afxwin_inline cwinapp* AFXAPI AfxGetApp ()
{return afxcurrentwinapp;}
#define Afxcurrentwinapp
Afxgetmodulestate ()->m_pcurrentwinapp
The original is to get the module associated with the current thread of execution, and get its containing/corresponding CWinThread object, and execute its message loop function. Now finally understand why each MFC-supported module has a CWinApp (derived from the CWinThread) of the global object, originally used for the message loop.
Note that if the current thread does not have a MODULE associated with it, it returns the default Afx_module_state object for the current process, see Afxgetmodulestate ().
Well, let's take a look at what's going on in the message loop.
BOOL CWinThread::P umpmessage ()
{
......
if (m_msgcur.message! = Wm_kickidle &&! PreTranslateMessage (&m_msgcur))
{
:: TranslateMessage (&m_msgcur);
::D ispatchmessage (&m_msgcur);
}
return TRUE;
BOOL CWinThread::P retranslatemessage (msg* pMsg)
{
......
Walk from Target to main window
cwnd* pMainWnd = AfxGetMainWnd ();
if (Cwnd::walkpretranslatetree (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);
The CWnd object that originally generated the Assert is the main window object, which is Ctestmfcdlg, not the CAboutDlg that we created behind the work line thread. This question makes us wonder why the Ctestmfcdlg object is called in the CABOUTDLG message loop. Looking closely at the note, the original message loop tries to find the so-called main window (AfxGetMainWnd), and then calls its message preprocessing function (PreTranslateMessage) after it finds it.
Let's see where the main window is.
_afxwin_inline cwnd* AFXAPI AfxGetMainWnd ()
{cwinthread* pThread = Afxgetthread ();
return pThread! = NULL? Pthread->getmainwnd (): NULL; }
cwnd* Cwinthread::getmainwnd ()
{
if (m_pActiveWnd! = NULL)
return m_pactivewnd;
Probably In-place active
When not inplace active, just return main window
if (m_pMainWnd! = NULL)
return m_pMainWnd;
return Cwnd::getactivewindow ();
}
It is clear that you want to get the main window/activation window for the CWinThread corresponding to the module associated with the current thread.
Because both the main thread and the worker thread do not switch work to the module, _afxthreadstate->m_pmodulestate is empty, so the result of two threads executing afxgetmodulestate () always returns the process default Afx_ The Module_state object, or _afxbasemodulestate. So the Afxgetthread () function returns necessarily the Theapp object (Ctestmfcapp), and the m_pMainWnd member of the latter point is the Ctestmfcdlg object.
When calling Ctestmfcdlg's PreTranslateMessage function, the following checks are made internally
cframewnd* cwnd::gettoplevelframe () const
{
if (getsafehwnd () = = NULL)//No Window attached
return NULL;
Assert_valid (this);
It then enters the previously mentioned Cwnd::assertvalid () function, which finally causes the assert to occur. Why is it. Because Afxmaphwnd () is run under a worker thread, and the m_pMainWnd corresponding Ctestmfcdlg is created under the main path, the hash table used is different. Therefore, the worker thread finds the CWnd object according to the HWND, and then automatically creates a new CWnd, which, of course, is different from m_pMainWnd, although the HWND is the same.
Now let's think about why MFC has to do the previous assert check. Why is it that MFC objects are required to be used only in the creation thread and not across threads? As the comment in the position of the assert says:
Note:if either of the above asserts fire and your are
Writing a multithreaded application, it's likely that
You have passed a C + + object from one thread to another
and has used that object in a-a-intended.
(Simple inline wrapper functions should is used)
//
In general, CWND objects should is passed by HWND
One thread to another. The receiving thread can wrap
The HWND with a CWnd object by using Cwnd::fromhandle.
//
It is Dangerou