MFC DLL资源动态切换
2012-08-13 15:29
190 查看
在MFC使用过程中,遇到DLL资源与主EXE资源冲突问题。
出现这样的Bug,一时无从下手。
报错位置在核心代码中dlgcore.cpp。
AfxFindResourceHandle查找资源文件时,本应在exe中的资源,结果返回了dll的句柄。
解决方法:
参照一篇文章,终于弄清楚了其中的来龙去脉。
文章来源:http://blog.sina.com.cn/s/blog_62bb83b10100jbdj.html。
AFX_MANAGE_STATE(AfxGetStaticModuleState())
先看一个例子:
1、创建一个动态链接到MFC DLL的规则DLL,其内部包含一个对话框资源。指定该对话框ID如下:
#define IDD_DLL_DIALOG 2000
2、创建一个基于对话框的mfc应用程序,它包含两个对话框资源,IDD_UI_DIALOG和IDD_EXE_DIALOG。并将后者的ID指定如下:
#define IDD_EXE_DIALOG 2000
其中前者是这个应用程序的用户界面,单击上面的按钮,将弹出一个对话框。部分代码如下:
// in DLL
void CDLL::ShowDlg(void)
{
CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框
dlg.DoModal();
}
// in EXE
void CEXE::OnButtonClick()
{
ShowDlg();
}
3、单击按钮,弹出的不是期望的DLL中的对话框IDD_DLL_DIALOG,而是应用程序中的对话框IDD_EXE_DIALOG。
解释:
1、应用程序进程本身及其调用的每个DLL模块都具有一个全局唯一的HINSTANCE句柄,它们代表了EXE或DLL模块在进程虚拟空间中的起始地址。(进程本身的模块句柄一般为0x400000,而DLL模块的缺省句柄为0x10000000。如果程序同时加载了多个DLL,则每个DLL模块都会有不同的HINSTANCE。应用程序在加载DLL时对其进行了重定位)。
2、共享MFC DLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE句柄对于加载资源特别重要。EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,如果应用程序与规则DLL共享MFC
DLL(或MFC扩展DLL),那么将总是默认使用EXE的资源。
3、因此应用程序需要通过资源模块的切换来找到正确的资源。如果应用程序需要来自于DLL的资源,就应将资源模块句柄指定为DLL的模块句柄;如果需要EXE文件中包含的资源,就应将资源模块句柄指定为EXE的模块句柄。
解决办法:
1、在DLL中改进:
方法1。
// in DLL
void CDLL::ShowDlg(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框
dlg.DoModal();
}
注:AFX_MANAGE_STATE(AfxGetStaticModuleState());一定是作为接口函数的第一条语句。
其功能是在栈上(这意味着其作用域是局部的)创建一个AFX_MODULE_STATE类的实例,并将其指 针pModuleState返回。
AFX_MODULE_STATE类利用其构造函数和析构函数进行存储模块状态现场及恢复现场的工作。
该宏用于将pModuleState设置为当前的有效模块状态。当离开该宏的作用域时(也就离开了pModuleState所指栈上对象的作用域),先前的模块状态将由类AFX_MODULE_STATE的析构函数恢复。(即自动恢复)
方法2。
// in DLL
void CDLL::ShowDlg(void)
{
HINSTANCE save_hInstance = AfxGetResourceHandle();
AfxSetResourceHandle(theApp.m_hInstance);
CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框
dlg.DoModal();
AfxSetResourceHandle(save_hInstance);
}
注:AfxGetResourceHandle:获取当前资源模块句柄;AfxSetResourceHandle:设置程序目前要使用的资源模块句柄。
同方法1比较,方法2能够灵活地设置程序的资源模块句柄,而方法1则只能在DLL接口函数退出的时候才会恢复模块句柄。
2、在应用程序中改进:
// in EXE
void CEXE::OnButtonClick()
{
HINSTANCE exe_hInstance = GetModuleHandle(NULL);
HINSTANCE dll_hInstance = GetModuleHandle("SharedDll.dll");
AfxSetResourceHandle(dll_hInstance); //切换状态
ShowDlg();
AfxSetResourceHandle(exe_hInstance); //恢复状态
}
注:使用状态切换的情况:当DLL导出函数包含MFC资源、类或者需要创建窗口时。
出现这样的Bug,一时无从下手。
报错位置在核心代码中dlgcore.cpp。
BOOL AFXAPI _AfxCheckDialogTemplate(LPCTSTR lpszResource, BOOL bInvisibleChild) { ASSERT(lpszResource != NULL); HINSTANCE hInst = AfxFindResourceHandle(lpszResource, RT_DIALOG); HRSRC hResource = ::FindResource(hInst, lpszResource, RT_DIALOG); if (hResource == NULL) { if (DWORD_PTR(lpszResource) > 0xffff) TRACE(traceAppMsg, 0, _T("ERROR: Cannot find dialog template named '%s'.\n"), lpszResource); else TRACE(traceAppMsg, 0, "ERROR: Cannot find dialog template with IDD 0x%04X.\n", LOWORD((DWORD_PTR)lpszResource)); return FALSE; } ...... return TRUE; }
AfxFindResourceHandle查找资源文件时,本应在exe中的资源,结果返回了dll的句柄。
解决方法:
//记录当前资源句柄 HINSTANCE hCurInstance = AfxGetResourceHandle(); //设置主模块资源句柄 AfxSetResourceHandle(theApp.m_hInstance); Create(CTestDlg::IDD, GetDesktopWindow()); ShowWindow(SW_HIDE); ShowWindow(SW_SHOWNOACTIVATE); //恢复当前模块句柄 AfxSetResourceHandle(hCurInstance);
参照一篇文章,终于弄清楚了其中的来龙去脉。
文章来源:http://blog.sina.com.cn/s/blog_62bb83b10100jbdj.html。
AFX_MANAGE_STATE(AfxGetStaticModuleState())
先看一个例子:
1、创建一个动态链接到MFC DLL的规则DLL,其内部包含一个对话框资源。指定该对话框ID如下:
#define IDD_DLL_DIALOG 2000
2、创建一个基于对话框的mfc应用程序,它包含两个对话框资源,IDD_UI_DIALOG和IDD_EXE_DIALOG。并将后者的ID指定如下:
#define IDD_EXE_DIALOG 2000
其中前者是这个应用程序的用户界面,单击上面的按钮,将弹出一个对话框。部分代码如下:
// in DLL
void CDLL::ShowDlg(void)
{
CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框
dlg.DoModal();
}
// in EXE
void CEXE::OnButtonClick()
{
ShowDlg();
}
3、单击按钮,弹出的不是期望的DLL中的对话框IDD_DLL_DIALOG,而是应用程序中的对话框IDD_EXE_DIALOG。
解释:
1、应用程序进程本身及其调用的每个DLL模块都具有一个全局唯一的HINSTANCE句柄,它们代表了EXE或DLL模块在进程虚拟空间中的起始地址。(进程本身的模块句柄一般为0x400000,而DLL模块的缺省句柄为0x10000000。如果程序同时加载了多个DLL,则每个DLL模块都会有不同的HINSTANCE。应用程序在加载DLL时对其进行了重定位)。
2、共享MFC DLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE句柄对于加载资源特别重要。EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,如果应用程序与规则DLL共享MFC
DLL(或MFC扩展DLL),那么将总是默认使用EXE的资源。
3、因此应用程序需要通过资源模块的切换来找到正确的资源。如果应用程序需要来自于DLL的资源,就应将资源模块句柄指定为DLL的模块句柄;如果需要EXE文件中包含的资源,就应将资源模块句柄指定为EXE的模块句柄。
解决办法:
1、在DLL中改进:
方法1。
// in DLL
void CDLL::ShowDlg(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框
dlg.DoModal();
}
注:AFX_MANAGE_STATE(AfxGetStaticModuleState());一定是作为接口函数的第一条语句。
其功能是在栈上(这意味着其作用域是局部的)创建一个AFX_MODULE_STATE类的实例,并将其指 针pModuleState返回。
AFX_MODULE_STATE类利用其构造函数和析构函数进行存储模块状态现场及恢复现场的工作。
该宏用于将pModuleState设置为当前的有效模块状态。当离开该宏的作用域时(也就离开了pModuleState所指栈上对象的作用域),先前的模块状态将由类AFX_MODULE_STATE的析构函数恢复。(即自动恢复)
方法2。
// in DLL
void CDLL::ShowDlg(void)
{
HINSTANCE save_hInstance = AfxGetResourceHandle();
AfxSetResourceHandle(theApp.m_hInstance);
CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框
dlg.DoModal();
AfxSetResourceHandle(save_hInstance);
}
注:AfxGetResourceHandle:获取当前资源模块句柄;AfxSetResourceHandle:设置程序目前要使用的资源模块句柄。
同方法1比较,方法2能够灵活地设置程序的资源模块句柄,而方法1则只能在DLL接口函数退出的时候才会恢复模块句柄。
2、在应用程序中改进:
// in EXE
void CEXE::OnButtonClick()
{
HINSTANCE exe_hInstance = GetModuleHandle(NULL);
HINSTANCE dll_hInstance = GetModuleHandle("SharedDll.dll");
AfxSetResourceHandle(dll_hInstance); //切换状态
ShowDlg();
AfxSetResourceHandle(exe_hInstance); //恢复状态
}
注:使用状态切换的情况:当DLL导出函数包含MFC资源、类或者需要创建窗口时。
相关文章推荐
- MFC DLL资源动态切换
- MFC DLL资源动态切换
- MFC DLL资源动态切换
- MFC DLL 资源模块句柄切换
- MFC扩展dll里导出函数时资源切换的问题
- DLL动态库中调用MFC资源
- MFC DLL 资源模块句柄切换[转]
- MFC DLL 资源模块句柄切换
- MFC扩展dll里导出函数时资源切换的问题
- MFC中CDialog与其对话框资源的绑定 dll中资源的切换
- MFC的资源切换AFX_MANAGE_STATE (转载)
- web,jsp,html网站中英文切换,资源国际化解决方案及代码(其中包含动态切换数据源及URL拦截器).
- MFC加载纯资源DLL ,常用于实现多语言
- 如何为 MFC 应用程序创建本地化资源 DLL
- MFC的资源切换AFX_MANAGE_STATE(AfxGetStaticModuleState())
- struts--动态访问国际化资源(国家/语言切换)
- VB动态加载纯DLL资源
- 关于如何调用mfc带资源对话框的dll的简单演示
- MFC的资源切换AFX_MANAGE_STATE(AfxGetStaticModuleState())
- 基于MFC的Windows多国语版本软件 —— 加载资源dll