我已经构建了以下配置:
从DLL B调用函数时,从DLL A内部调用函数来显示对话框时,由于无法找到资源而发生错误。
我已经挖掘找到确切的根本原因,并且主要的共鸣似乎是模块上下文被设置为调用dll B而不是包含对话框资源的DLL A.
在DllMain内部,初始化按照MSDN中所述完成:
static AFX_EXTENSION_MODULE NEAR extensionDLL = { NULL, NULL };
extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
Hinstance = hInstance; //save instance for later reuse
// Extension DLL one-time initialization
if (AfxInitExtensionModule(extensionDLL,hInstance) == 0)
{
AfxMessageBox("Error on init AfxInitExtensionModule!");
return 0;
}
// Insert this DLL into the resource chain
new CDynLinkLibrary(extensionDLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
Release();
}
return 1;
}
我发现的一个workarround是存储从DLLMain收到的 hInstance 参数: extern“C”int APIENTRY DllMain(HINSTANCE hInstance ,DWORD dwReason ,LPVOID lpReserved) 在DLL中调用函数时,我保存当前句柄并设置从DllMain收到的句柄的新句柄:
DLL A function1(............)
{
HINSTANCE HinstanceOld = AfxGetResourceHandle();
AfxSetResourceHandle(CErrohInstance);
.......
//display dialog
.....
AfxSetResourceHandle(HinstanceOld);
}
通过使用此workarround它仍然会引发断言,但会显示对话框。
解决这个问题的正常方法是什么?
答案 0 :(得分:3)
您必须将扩展DLL的资源插入到常规DLL的资源链中,而不是EXE。只需在扩展DLL中创建一个函数,并在常规DLL的InitInstance方法中调用它,如下所示:
void initDLL()
{
new CDynLinkLibrary(extensionDLL);
}
答案 1 :(得分:1)
你说"模块上下文"但事实上,终端技术是"模块状态"。
AFAICS这是相对标准(即最常发生)的MFC模块状态相关用例,即:通过回调/公共导出API进入内部实现区域。
AFX_MANAGE_STATE直接提到了这个用例:"如果你在DLL中有导出的函数"
此时,当前处于活动状态的模块状态是主叫方之一,它不是实现范围内所需的那个。 由于实现范围知道它需要一个不同的模块状态(并且它是知道哪一个是正确的!),它需要临时切换到正确的模块状态! ,实现在正确的实例范围内完成任何与模块状态相关的查找(精确地:资源实例查找)。
这不需要通过AfxSetModuleState()手动完成,而是通过正确的生命周期范围(保证适当的破坏,在任何可能存在的取消点,无论是返回还是异常等)AFX_MANAGE_STATE宏的机制。 / p>
IOW,实现可能需要非常类似于:
BOOL MyPublicAPIOrCallback()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState()); // ensure locally expected module state within this externally-invoked handling scope
some_handling_which_does_resource_lookup_or_whatever;
}
答案 2 :(得分:0)
答案 3 :(得分:0)
我在我的DLLMain中添加了这些行,现在我没有问题使用我的DLL调用的其他DLL中的资源,比如对话框。 这是代码:
static AFX_EXTENSION_MODULE CODIAbantailDLLDLL = { NULL, NULL };
AplicacionBase theApp;
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
// Quitar lo siguiente si se utiliza lpReserved
UNREFERENCED_PARAMETER(lpReserved);
if (dwReason == DLL_PROCESS_ATTACH)
{
// ******** VERY IMPORTANT ***********************
// IF you doesn't put this, when you call other DLL that has
// its owns resources (dialogs for instance), it crash
CoInitialize(NULL);
AfxWinInit(hInstance, NULL, ::GetCommandLine(), 0);
AfxEnableControlContainer();
//**************************************************
TRACE0("Inicializando CODIAbantailDLL.DLL\n");
// Inicialización única del archivo DLL de extensión
if (!AfxInitExtensionModule(CODIAbantailDLLDLL, hInstance))
return 0;
new CDynLinkLibrary(CODIAbantailDLLDLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("Finalizando CODIAbantailDLL.DLL\n");
// Finalizar la biblioteca antes de llamar a los destructores
AfxTermExtensionModule(CODIAbantailDLLDLL);
}
return 1; // aceptar
}