MFC dll的初始化过程及关于模块状态的错误

时间:2021-10-08 19:48:42

我在做一个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