AfxGetThread returning NULL in Regular DLLs
I am currently in the process of migrating some UI DLLs from VC6/MFC4
to VS2005/MFC8. I have hit a backward-compatibility issue that seems to
suggest that either something is very wrong in MFC8 or there are some
wrong assumptions in my DLLs that MFC4 doesn't catch.
My app/DLL arrangement is as follows:
[Third-party app].exe -- dynamically loads --> MyWin32DLL.dll --
statically linked to --> MyMFCDLL.dll
MyWin32DLL.dll is a pretty standard Win32 (UI) DLL whereas MyMFCDLL.dll
is a Regular (UI) DLL which is linked statically with MFC (although
with regard to the following problem it doesn't actually matter whether
I link MyMFCDLL with MFC statically or dynamically).
As shown above, MyWin32DLL.dll is bound to the main app at run-time
(using LoadLibrary etc.); MyWin32DLL is statically linked to
MyMFCDLL.dll.
MyMFCDLL is a regular DLL which has a static CWinApp instance. The
CWinApp constructor is therefore called when MyWin32DLL is loaded
(since MyMFCDLL is statically linked to it).
Within that constructor,the standard AFX state initialization takes
place
// initialize CWinThread state
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
ENSURE(pModuleState);
AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
ENSURE(pThreadState);
ASSERT(AfxGetThread() == NULL);
pThreadState->m_pCurrentWinThread = this;
ASSERT(AfxGetThread() == this);
m_hThread = ::GetCurrentThread();
m_nThreadID = ::GetCurrentThreadId();
but note that the current thread will be whatever thread is used to
load MyMFCDLL (automatically when MyWin32DLL is loaded).
Later, when MyWin32DLL needs to call the functions exported by
MyMFCDLL, another thread (out of my control) is used i.e. the UI thread
of MyWin32DLL.
[Remember the golden rule that a window's message processing must take
place on the same thread that was used to created the window - there is
no reference to CWinApp construction in that though that I'm aware of]
All my exported functions in MyMFCDLL.DLL do call
AFX_MANAGE_STATE(AfxGetStaticModuleState()) as required (and are called
by MyWin32DLL on the same thread).
One of MyMFCDLL's exported functions results in displaying a modal
property sheet and that works fine except...
in the property sheet's OnHelpInfo implementation which looks like:
BOOL CMyPropertySheet::OnHelpInfo(HELPINFO* pHelpInfo)
{
if (pHelpInfo->iContextType == HELPINFO_WINDOW) {
AfxGetApp()->WinHelp(pHelpInfo->dwContextId,
HELP_CONTEXTPOPUP);
}
return(TRUE);
}
This in turn causes the following to be called:
void CWinApp::WinHelp(DWORD_PTR dwData, UINT nCmd)
{
CWnd* pMainWnd = AfxGetMainWnd();
ENSURE_VALID(pMainWnd);
.
.
}
The problem is that with MFC4 this all works, however with MFC8
AfXGetMainWnd() returns NULL!
Now it returns NULL because it relies on AfxGetThread which itself
returns NULL under MFC8 but is OK with MFC4.
MFC8's version of AfxGetThread is totally reliant on the
AFX_MODULE_THREAD_STATE instance (returned by AfxGetModuleThreadState)
containing a valid m_pCurrentWinThread. In my scenaio this is not the
case.
With MFC4, if the m_pCurrentWinThread of the AFX_MODULE_THREAD_STATE
instance is NULL, then AfxGetThread simply uses AfxGetApp to return a
valid CWinThread pointer instead.
Now since MyMFCDLL's CWinApp instance was initialised on a different
thread to the UI thread that is subsequently used, the pThreadState var
in the CWinApp constructor will not be the same as what's later
returned by AfxGetModuleThreadState (i.e. when called in the message
processing methods) even though the same AFX_MODULE_STATE is referenced
in both cases.
I can work around this problem for now using
BOOL CMyPropertySheet::OnHelpInfo(HELPINFO* pHelpInfo)
{
if (pHelpInfo->iContextType == HELPINFO_WINDOW) {
AfxGetApp()->GetMainWnd()->WinHelp(pHelpInfo->dwContextId,
HELP_CONTEXTPOPUP);
}
return(TRUE);
}
however, there is obviously a underlying more serious problem somewhere
that actually needs addressing.
So,
1) why was the AfxGetApp call removed from AfxGetThread in MFC8? Was it
just to catch these sort of cases?
2) Shouldn't the AFX_MODULE_THREAD_STATE returned by
AfxGetModuleThreadState when called from the UI thread have its
m_pCurrentWinThread member already set to something other than NULL and
if so where should that have been initialised i.e. what step is
missing?
The problem of AfxGetThread potentially returning NULL has been
mentioned in other posts but I can find no explanation of the
underlying problem/reason.
Any ideas?
Thanks
Will