MFC DLL资源动态切换

简介: 在MFC使用过程中,遇到DLL资源与主EXE资源冲突问题。 出现这样的Bug,一时无从下手。 报错位置在核心代码中dlgcore.cpp。 [cpp] view plaincopy BOOL AFXAPI _AfxCheckDialogTemplate(LPCTST...

在MFC使用过程中,遇到DLL资源与主EXE资源冲突问题。

出现这样的Bug,一时无从下手。

报错位置在核心代码中dlgcore.cpp。

[cpp]   view plain copy
  1. BOOL AFXAPI _AfxCheckDialogTemplate(LPCTSTR lpszResource, BOOL bInvisibleChild)  
  2. {  
  3.     ASSERT(lpszResource != NULL);  
  4.     HINSTANCE hInst = AfxFindResourceHandle(lpszResource, RT_DIALOG);  
  5.     HRSRC hResource = ::FindResource(hInst, lpszResource, RT_DIALOG);  
  6.     if (hResource == NULL)  
  7.     {  
  8.         if (DWORD_PTR(lpszResource) > 0xffff)  
  9.             TRACE(traceAppMsg, 0, _T("ERROR: Cannot find dialog template named '%s'.\n"),  
  10.                 lpszResource);  
  11.         else  
  12.             TRACE(traceAppMsg, 0, "ERROR: Cannot find dialog template with IDD 0x%04X.\n",  
  13.                 LOWORD((DWORD_PTR)lpszResource));  
  14.         return FALSE;  
  15.     }  
  16.           
  17.         ......  
  18.           
  19.         return TRUE;  
  20. }  

AfxFindResourceHandle 查找资源文件时,本应在exe中的资源,结果返回了dll的句柄。

解决方法:

[cpp]   view plain copy
  1. //记录当前资源句柄  
  2. HINSTANCE hCurInstance = AfxGetResourceHandle();  
  3.   
  4. //设置主模块资源句柄  
  5. AfxSetResourceHandle(theApp.m_hInstance);  
  6.   
  7. Create(CTestDlg::IDD, GetDesktopWindow());  
  8. ShowWindow(SW_HIDE);  
  9. ShowWindow(SW_SHOWNOACTIVATE);  
  10.   
  11. //恢复当前模块句柄  
  12. 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资源、类或者需要创建窗口时。


目录
相关文章
|
Linux 编译器 C++
C/C++性能优化:从根本上消除拷贝操作的浪费
C/C++性能优化:从根本上消除拷贝操作的浪费
1517 1
|
网络协议 安全 网络安全
Windows下配置OpenSSL(傻瓜式教程,一看就会)
Windows下配置OpenSSL(傻瓜式教程,一看就会)
3344 0
|
Unix 编译器 开发者
Qt5.14.2 轻松掌握Qt中的压缩与解压缩:QuaZIP的神秘面纱与实战演练之windows环境编译
Qt5.14.2 轻松掌握Qt中的压缩与解压缩:QuaZIP的神秘面纱与实战演练之windows环境编译
1139 0
|
10月前
|
缓存 负载均衡 Java
Java“TimeoutException”解决
Java中的“TimeoutException”通常在操作超时未完成时抛出。解决方法包括:增加超时时间、优化代码逻辑减少执行时间、使用异步处理或线程池提高效率。
993 11
|
9月前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
10月前
|
前端开发 开发者
鸿蒙next版开发:相机开发-元数据(ArkTS)
在HarmonyOS 5.0中,ArkTS新增了对相机元数据的访问能力,帮助开发者获取图像的详细信息。本文介绍了如何在ArkTS中获取和使用相机元数据,包括导入接口、创建元数据输出流、开启和停止元数据输出、监听元数据对象可用事件等步骤,并提供了详细的代码示例。
222 5
|
11月前
|
监控 关系型数据库 MySQL
一次彻底讲清如何处理mysql 的死锁问题
【10月更文挑战第16天】本文详细介绍了如何处理 MySQL 中的死锁问题,涵盖死锁的概念、原因、检测方法及解决策略,强调通过优化事务设计、调整数据库参数、手动处理和预防措施等手段,有效减少死锁,提升数据库性能与稳定性。
2009 0
|
编译器 开发工具 C语言
解锁QtCreator跨界神技!Windows下轻松驾驭OpenCV动态库,让你的跨平台开发如虎添翼,秒变视觉编程大师!
【8月更文挑战第4天】QtCreator是一款强大的跨平台IDE,便于创建多平台应用。本教程教你如何在Windows环境下集成OpenCV库至Qt项目。首先,下载匹配MinGW的OpenCV预编译版并解压。接着,在QtCreator中新建或打开项目,并在.pro文件中添加OpenCV的头文件和库文件路径。确保编译器设置正确。随后编写测试代码,例如加载和显示图片,并进行编译运行。完成这些步骤后,你就能在QtCreator中利用OpenCV进行图像处理开发了。
614 6
用free命令看到的cache跟/proc/meminfo看到的为什么不同?
用free命令看到的cache跟/proc/meminfo看到的为什么不同?
|
机器学习/深度学习 人工智能 搜索推荐
【AI 场景】如何使用 AI 向客户推荐个性化产品?
【5月更文挑战第3天】【AI 场景】如何使用 AI 向客户推荐个性化产品?
【AI 场景】如何使用 AI 向客户推荐个性化产品?