我在做一个mfc dll的过程中遇到了一个棘手的模块状态设置错误的问题,跟踪了很久,看了很多帖子和书籍最终解决了这个问题。
由于第一个帖子是在codeguru上发的,所以就索性一直用英文记录整个过程了,我的问题记录如下:
===================================== seperate line =========================================
I'm working on a project that's using a mfc regular dll from a Qt gui project(yes, you don't see wrong), the mfc dll code is extracted from some other projects build entirely in mfc. I build all the mfc code with a regular dll and run it, and it came to the debug assertion during the initialization at the code:
ASSERT(AfxGetModuleState() != AfxGetAppModuleState());
and
VERIFY(AfxSetModuleState(AfxGetThreadState()->m_pPrevModuleState) == &afxModuleState);
furthermore, when i ignore and continue running the programme, a debug assertion failed at the code:
ASSERT(afxCurrentResourceHandle != NULL)
of course the resource handle is null.
I track down the source code, and it seems that during the initialization, the 'dllmain'(in fact there is a lot of 'dllmains') in my dll has been invoked twice and only the seconde time the assert has been emitted, the first time it has been running ok. and I notice that the members in the global variable afxModuleState has all been null thus cause the assertion after the initialization, and what's more I notice that during the first time initialization, the afxModuleState has been assigned right.
I wonder what's the cause of my problem and how to solve it. And I'm willing to hear your advice.
an additional information is that, in my regular dll, I use boost library(which I'm not too familiar with)
===================================== seperate line =========================================
after the first tip posted on codeguru and no reply, i tack source code of the mfc dll startup and the following it the details:
the fisrst time initialization call stack:
mfc80d.dll!AfxWinInit
mfc80d.dll!DllMain
mfc80d.dll!__DllMainCRTStartup
mfc80d.dll!_DllMainCRTStartup
the second time initialization call stack:
GemsDLL.dll!InternalDllMain
GemsDLL.dll!DllMain
GemsDLL.dll!__DllMainCRTStartup
GemsDLL.dll!_DllMainCRTStartup
the call stack when the assert 'ASSERT(AfxGetModuleState() != AfxGetAppModuleState())' occurs:
mfc80d.dll!AfxCoreInitModule
GemsDLL.dll!InternalDllMain
GemsDLL.dll!DllMain
GemsDLL.dll!__DllMainCRTStartup
GemsDLL.dll!_DllMainCRTStartup
in code
AFX_MODULE_STATE* AFXAPI AfxGetModuleState()
{
_AFX_THREAD_STATE* pState = _afxThreadState;
ENSURE(pState);
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();
}
ENSURE(pResult != NULL);
return pResult;
}
the pState is different from the first time initialization(the first time is not my mfc dll?), and all its value is null
the second time call stack:
mfc80d.dll!AfxWinInit
GemsDLL.dll!InternalDllMain
GemsDLL.dll!DllMain
GemsDLL.dll!__DllMainCRTStartup
GemsDLL.dll!_DllMainCRTStartup
and the AfxWinInit code:
BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,
__in LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
// handle critical errors and avoid Windows message boxes
SetErrorMode(SetErrorMode(0) |
SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
// set resource handles
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_hCurrentInstanceHandle = hInstance;
pModuleState->m_hCurrentResourceHandle = hInstance;
pModuleState->CreateActivationContext();
// fill in the initial state for the application
CWinApp* pApp = AfxGetApp();
if (pApp != NULL)
{
// Windows specific initialization (not done if no CWinApp)
pApp->m_hInstance = hInstance;
hPrevInstance; // Obsolete.
pApp->m_lpCmdLine = lpCmdLine;
pApp->m_nCmdShow = nCmdShow;
pApp->SetCurrentHandles();
}
// initialize thread specific data (for main thread)
if (!afxContextIsDLL)
AfxInitThread();
// Initialize CWnd::m_pfnNotifyWinEvent
HMODULE hModule = ::GetModuleHandle(_T("user32.dll"));
if (hModule != NULL)
{
CWnd::m_pfnNotifyWinEvent = (CWnd::PFNNOTIFYWINEVENT)::GetProcAddress(hModule, "NotifyWinEvent");
}
return TRUE;
}
it seems that the first time initializatio is called from the mfc80d.dll which maybe the core dll for mfc, and the second time initialization is called rom my mfc dll, in the project, it is called 'GemsDll.dll'. I remember that the mfc core dll should be an extension dll, as to some deeper details and thoughts, I haven't got much yet.
and i find when the first assertion 'ASSERT(AfxGetModuleState() != AfxGetAppModuleState())' occurs,
the AfxGetModuleState() returns:
_AFX_THREAD_STATE* pState = _afxThreadState;
ENSURE(pState);
AFX_MODULE_STATE* pResult;
if ...
else
{
// otherwise, use global app state
pResult = _afxBaseModuleState.GetData();
}
ENSURE(pResult != NULL);
return pResult;
value: m_pObject 0x001d4a48
the AfxGetAppModuleState() returns
return _afxBaseModuleState.GetData();
and its value: m_pObject 0x001e4a48
0x001d4a48 and 0x001e4a48, a little different.
in AfxWinInit,
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_hCurrentInstanceHandle = hInstance;
pModuleState->m_hCurrentResourceHandle = hInstance;
pModuleState->CreateActivationContext();
the pModuleState is 'pResult = _afxBaseModuleState.GetData();', and the address is 0x001d4a48, it seems that the initialization goes to the _afxBaseModuleState variable
===================================== seperate line =========================================
In a project that I made up my own with clean code just using the CString class in mfc (all the initialization and module state switch process is right without the problems mentioned), I find that in function 'BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance, __in LPTSTR lpCmdLine, int nCmdShow)',
BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,
__in LPTSTR lpCmdLine, int nCmdShow)
{
...
// set resource handles
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_hCurrentInstanceHandle = hInstance;
pModuleState->m_hCurrentResourceHandle = hInstance;
pModuleState->CreateActivationContext();
...
return TRUE;
}
the line 'AFX_MODULE_STATE* pModuleState = AfxGetModuleState();' returns the moduleState member variable in threadState, which in the dll where problems occurred, the function returns '_afxBaseModuleState.GetData();'. What's more, pModuleState value in the clean dll points right to the global variable afxModuleSatet! But why moduleState member variable in threadState differs in two dlls? WHEN IS THE MDDULESTATE MEMBER IN THREADSTATE SET? I didn't see any init code for the threadState in the 'dllmains' I thus far track. So I decided to track the init process for threadState.
Moreever, in 'InternalDllMain', pState get from the sentence '_AFX_THREAD_STATE* pState = AfxGetThreadState();' already holds a right moduleState.
extern "C"
BOOL WINAPI InternalDllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
BOOL bResult = FALSE;
#ifdef _AFXDLL
// wire up resources from core DLL
AfxCoreInitModule();
#endif
_AFX_THREAD_STATE* pState = AfxGetThreadState();
AFX_MODULE_STATE* pPrevModState = pState->m_pPrevModuleState;
// Initialize DLL's instance(/module) not the app's
if (!AfxWinInit(hInstance, NULL, _T(""), 0))
{
AfxWinTerm();
goto Cleanup; // Init Failed
}
...
return bResult;
}
...
}
===================================== seperate line =========================================
I may have found the cause. In blog about the mfc module state(http://hi.baidu.com/rootlife/blog/item/2f37e354ad8cdc5bd10906be.html), there is such words:
'AfxGetModuleState首先获得_afxThreadState的m_pModuleState,如果当前的Thread State的m_pModuleState返回NULL,说明当前的ThreadState没有正确的初始化(通常的原因是创建线程的时候调用的是CreateThread函数而非AfxBeginThread),则使用_afxBaseModuleState。'
My main project is written in Qt, which in fact was purely C++ with some marcros, libs, dlls and the moc generator. I guess the thread is created with CreateThread instead of AfxBeginThread, so the _afxThreadState is not properly initialized. Consider that the way of thread creating cannot be modified in Qt, I decided to manually set the resource handle in my Dll export function. The thought came to me when I saw the function specification 'HMODULE GetModuleHandle(PCTSTR pszModule);' in 'Windows Via C/C++' which returns a base address a module is loaded in the memory by the module name specified. I call it and set the resource handle with AfxSetResourceHandle and it seems to me now it is right, all the MFC funcitons work properly. Though the initialzation assertions still hang unresolved, as least it can run now.
参考过的帖子如下:
http://support.microsoft.com/kb/q148791/en HOWTO: How to Provide Your Own DllMain in an MFC Regular DLL
http://www.vczx.com/tutorial/mfc/mfc9.php MFC的状态
http://hi.baidu.com/rootlife/blog/item/2f37e354ad8cdc5bd10906be.html MFC的模块状态
http://blog.csdn.net/solotony/article/details/43847 MFC初始化过程
http://www.eggheadcafe.com/microsoft/VC-ATL/35495571/assertions-during-dll-load.aspx 同样的问题
http://www.cnblogs.com/moonz-wu/archive/2008/05/08/1189021.html 不懂就要学,windows编程
http://social.msdn.microsoft.com/Forums/en/vcgeneral/thread/ec0ce12e-d54d-4b6a-944e-62dd8bb76b7d Debug Assertion failed error while using an mfc dll via import library in an managed console application
http://www.codeproject.com/KB/miscctrl/HostMFC.aspx Hosting of MFC MDI Applications from Within WinForms and WPF Applications
http://lists.boost.org/threads-devel/2009/06/0476.php Re: [Threads-devel] link problem: _pRawDllMain conflict issue