zoukankan      html  css  js  c++  java
  • MFC dll的初始化过程及关于模块状态的错误

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


    ____________________________
    本博客文章主要供博主学习交流用,所有描述、代码无法保证准确性,如有问题可以留言共同讨论。
  • 相关阅读:
    通过DialogFragment从DatePicker或TimePicker中获取日期数据
    解决android.os.NetworkOnMainThreadException
    关于在Java EE 下开发web,出现项目中的外部包没有tomcat的包的原因
    HTTP method GET is not supported by this URL(转)
    tomcat安装和基本配置
    ubuntu 下面手动安装jdk
    用django搭建一个简易blog系统(翻译)(四)
    用django搭建一个简易blog系统(翻译)(三)
    用django搭建一个简易blog系统(翻译)(二)
    用django搭建一个简易blog系统(翻译)(一)
  • 原文地址:https://www.cnblogs.com/waytofall/p/2317138.html
Copyright © 2011-2022 走看看