【问题标题】:MFC state invalid when DLL called through LoadLibrary通过 LoadLibrary 调用 DLL 时 MFC 状态无效
【发布时间】:2012-01-20 06:48:46
【问题描述】:

我正在使用 MFC 并动态链接 DLL 与 LoadLibrary。当应用程序调用 DLL 时,我似乎无法正确获取 MFC 状态,并且 DLL 在同一个调用中回调。最终,它会导致大量断言。

这是我正在做的代码模型。

  1. 应用程序很正常,直接来自向导 MFC 应用程序。我在某处有按钮,这是按钮的处理程序:

    void callback()
    {
        AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
    
        CDialog1 dlg;
        dlg.DoModal();
    }
    
    typedef void (*TPluginMainFunc)(void*);
    
    void CTheApp1View::OnTestRun1()
    {
            static HMODULE hPluginMFCShared = LoadLibrary( _T("PluginMFCShared") );
            if ( hPluginMFCShared )
            {
                    TPluginMainFunc func = (TPluginMainFunc) GetProcAddress( hPluginMFCShared, "plugin_main" );
                    if ( func )
                    {
                            func(callback);
                    }
            }
    }
    
  2. 那么“PluginMFCShared”看起来像这样:

    typedef void (*TFunc)();
    
    extern "C" void GS_EXTERNAL_ENTRY plugin_main(TFunc func)
    {
            AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
    
            func();
    
            CDialog1 dlg;
            dlg.DoModal();
    }
    

所以,这个想法是应用程序 (CTheApp1View::OnTestRun1) 加载一个库并调用一个直接传入回调指针的函数。在继续之前,库将使用该回调从应用程序中执行某些操作。

我认为 AFX_MANAGE_STATE 会处理 MFC 状态,但似乎还有更多工作要做。

可以在以下位置找到一个测试项目(确保将 TheApp1 项目设置为启动项目): SystemOfPlugins.zip

有什么想法吗?

感谢您的任何建议。

【问题讨论】:

  • CDialog1 是在 DLL、应用程序还是两者中定义?您似乎在这两个地方都使用它,这将是一个问题。 DoModal 需要处于定义它的任何位置的状态。
  • 是的,CDialog1是在app和DLL中分别定义的,是完全不同的对话框资源。这两个项目(应用程序和 DLL)没有任何共同之处。代码 sn -p 只是为了演示案例。

标签: dll mfc shared-libraries


【解决方案1】:

我查看了您的代码,并通过修改 2 个函数使其工作:

在pluginMFCShared.cpp中,我在调用func()之后调用了AFX_MANAGE_STATE

extern "C" void GS_EXTERNAL_ENTRY plugin_main(TFunc func)
{

    func();

    AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
    CDialog1 dlg;
    dlg.DoModal();
}

在app1view.cpp中,我删除了AFX_MANAGE_STATE

void callback()
{
    CDialog1 dlg;
    dlg.DoModal();
}

现在,两个对话框一个接一个地弹出

【讨论】:

  • 感谢您的回答,它确实解决了问题。然而,这是一个简化的例子。实际上,该应用程序提供了一组回调函数,并且“plugin_main”可以随时运行回调,因此定义状态并不是那么简单。您知道在这种情况下管理 MFC 状态的最佳方法是什么吗?
【解决方案2】:

这是另一个建议。在您的 App 变量中,添加一个名为 m_pModuleState 的 AFX_MODULE_STATE* 变量,并在 InitInstance 函数的末尾对其进行初始化,

m_pModuleState = AfxGetModuleState();

修改你的回调函数,在打开对话框之前设置应用程序状态,然后在退出函数之前设置回原来的状态

void callback()
{
    //Get the original state
    AFX_MODULE_STATE* pOriginalState = AfxGetModuleState();

    //Set the mfc state
    AfxSetModuleState(((CTheApp1App*)&theApp)->m_pModuleState);

    //Do stuff here
    CDialog1 dlg;
    dlg.DoModal();

    //Set the mfc state back to its original state
    AfxSetModuleState(pOriginalState);
}

并保持您的插件与您的示例一样

extern "C" void GS_EXTERNAL_ENTRY plugin_main(TFunc func)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState( ));

    func();
    CDialog1 dlg;
    dlg.DoModal();
}

这样,您可以在插件中调用 AFX_MANAGE_STATE,但是当某些插件调用回调函数时,请确保设置应用程序的状态,以便它可以找到好的对话框资源并执行特定于状态的函数

【讨论】:

  • 我试过你的解决方案,它确实工作正常,它确实解决了资源问题。然而,似乎还有更多,如果我尝试使用其他 MFC 功能,它会因为类似的原因而失败.. MFC 的“状态”有问题。
  • 啊,抱歉,我过早地发布了之前的评论。我已经从问题中更新了原始样本。我已经应用了修复资源的更改,但我又添加了一个使用 MFC 的 DYNAMIC_DOWNCAST 的函数“GetMainFrame”。如果在“OnTestRun1”中使用此函数,它可以正常工作,但在“回调”中使用时,它不会......它无法解析类层次结构。
  • 我改进了我的答案。我不仅更改了资源状态,还更改了整个 mfc 模块状态。我认为这应该可以解决您的问题
【解决方案3】:

您是否使用 _LIB 预处理器标志构建 dll?如果是这样,请检查您是否真的应该 - 整个“MFC dll”概念已经过时,没有理由再使用它了。然后,忘记所有 AFX_MANAGE_STATE 的东西。在您的 dll 中,存储传递给 DllMain 的 dll 的 HMODULE,并在每次使用 CDialog 或类似内容之前使用 ::AfxSetResourceHandle() 为正确的值。将它包装在一个类似智能指针的类中,该类设置正确的资源句柄并在对象超出范围时将其重置为旧的(= 主应用程序的,通常为 0x4000...)。

对于您可以直接传递资源句柄(LoadString 等)的所有目的,您甚至不必触摸全局句柄。

更容易工作,更透明。无论如何,资源句柄是自 VS6 以来 MFC 版本中唯一与 MFC 状态相关的句柄。

【讨论】:

  • 不,我正在使用 _USRDLL 进行编译。
  • 是的,对不起,这就是我的意思。只是转储整个 AFX_MANAGE_STATE 的东西,它不必要地复杂(在 2011 年)。
猜你喜欢
  • 1970-01-01
  • 2015-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-08
相关资源
最近更新 更多