DLL详解(下)---MFC下的DLL
(本文针对的是使用MFC下的DLL)
MFC规则DLL
何为规则DLL(Regular DLL)
(1)该DLL是基于MFC的,可以在这种DLL内部使用MFC。
(2)该DLL是"规则"的,它不同于"MFC扩展DLL",在规则DLL中内部虽然是可以使用MFC,但是规则DLL的接口应该不能是基于MFC的。而MFC扩展DLL与应用程序接口可以是MFC,可以从MFC扩展dll
中导出一个MFC的派生类。
Regular DLL能够被所有支持DLL技术的语言所编写的应用程序调用,当然也包括使用MFC的应用程序。在这种动态连接库中,包含一个从CWinApp
继承下来的类,DllMain
函数则由MFC自动提供。
共享MFC DLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE句柄对于加载资源特别重要。EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,应用程序需要通过资源模块的切换来找到正确的资源。如果应用程序需要来自于DLL的资源,就应将资源模块句柄指定为 DLL的模块句柄;如果需要EXE文件中包含的资源,就应将资源模块句柄指定为EXE的模块句柄。(解释:EXE模块在进程虚拟空间中的起始地址 。 进程本身的模块句柄一般为0x400000 , 而DLL模块的缺省句柄为0x10000000 。 如果程序同时加载了多个DLL,则每个DLL模块都会有不同的 HINSTANCE 。 应用程序在加载DLL时对其进行了重定位。)
模块的切换有三种方式:
-
在DLL函数中调用
AFX_MANAGE_STATE(AfxGetStaticModuleState());
void ShowDlg(void) { //在函数开始处变更,在函数结束时恢复 //将AFX_MANAGE_STATE(AfxGetStaticModuleState()); //作为接口函数的第一条语句进行模块状态切换 AFX_MANAGE_STATE(AfxGetStaticModuleState()); CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框 dlg.DoModal(); }
-
在DLL函数中调用
AfxGetResourceHandle();
AfxGetResourceHandle
用于获取当前资源模块句柄,而AfxSetResourceHandle
则用于设置程序目前要使用的资源模块句柄。我们将DLL中的接口函数
ShowDlg
改为:void ShowDlg(void) { //方法2的状态变更 HINSTANCE save_hInstance = AfxGetResourceHandle(); AfxSetResourceHandle(theApp.m_hInstance); CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框 dlg.DoModal(); //方法2的状态还原 AfxSetResourceHandle(save_hInstance); }
通过
AfxGetResourceHandle
和AfxSetResourceHandle
的合理变更,我们能够灵活地设置程序的资源模块句柄,而方法一则只能在DLL接口函数退出的时候才会恢复模块句柄。方法二则不同,如果将ShowDlg
改为:extern CSharedDllApp theApp; //需要声明theApp外部全局变量 void ShowDlg(void) { //方法2的状态变更 HINSTANCE save_hInstance = AfxGetResourceHandle(); AfxSetResourceHandle(theApp.m_hInstance); CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框 dlg.DoModal(); //方法2的状态还原 AfxSetResourceHandle(save_hInstance); //使用方法2后在此处再进行操作针对的将是应用程序的资源 CDialog dlg1(IDD_DLL_DIALOG); //打开ID为2000的对话框 dlg1.DoModal(); }
在应用程序主对话框的“调用DLL”按钮上点击,将看到两个对话框,相继为DLL中的对话框(图6)和EXE中的对话框(图7)。
-
由应用程序自身切换(不推荐,最麻烦)
资源模块的切换除了可以由DLL接口函数完成以外,由应用程序自身也能完成。
现在我们把DLL中的接口函数改为最简单的:
void ShowDlg(void) { CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框 dlg.DoModal(); }
而将应用程序的
OnCalldllButton
函数改为:void CSharedDllCallDlg::OnCalldllButton() { //方法3:由应用程序本身进行状态切换 //获取EXE模块句柄 HINSTANCE exe_hInstance = GetModuleHandle(NULL); //或者HINSTANCE exe_hInstance = AfxGetResourceHandle(); //获取DLL模块句柄 HINSTANCE dll_hInstance = GetModuleHandle("SharedDll.dll"); AfxSetResourceHandle(dll_hInstance); //切换状态 ShowDlg(); //此时显示的是DLL的对话框 AfxSetResourceHandle(exe_hInstance); //恢复状态 //资源模块恢复后再调用ShowDlg ShowDlg(); //此时显示的是EXE的对话框 }
方法三中的Win32函数
GetModuleHandle
可以根据DLL的文件名获取DLL的模块句柄。如果需要得到EXE模块的句柄,则应调用带有Null参数的GetModuleHandle
。 方法三与方法二的不同在于,方法三是在应用程序中利用
AfxGetResourceHandle
和AfxSetResourceHandle
进行资源模块句柄切换的。同样地,在应用程序主对话框的“调用DLL”按钮上点击,也将看到两个对话框,相继为DLL中的对话框和EXE中的对话框。
MFC规则DLL的创建
MFC规则DLL并不是MFC应用程序,它所继承自CWinApp的类不包含消息循环。这是因为,MFC规则DLL不包含CWinApp::Run机制,主消息泵仍然由应用程序拥有。如果DLL 生成无模式对话框或有自己的主框架窗口,则应用程序的主消息泵必须调用从DLL 导出的函数来调用PreTranslateMessage成员函数。
(内存泄漏的一种可能原因是MFC创建了在消息处理函数内部使用的临时对象。在MFC应用程序中,这些临时对象将CWinApp::OnIdle()
在处理消息之间调用的函数中自动清除。但是,在MFC动态链接库(DLL)中,OnIdle()
不会自动调用该函数。结果,临时对象不会自动清除。若要清除临时对象,DLL必须OnIdle(1)
定期显式调用。)
在进程附加时,该_DllMainCRTStartup
函数设置缓冲区安全性检查,初始化CRT和其他库,初始化运行时类型信息,初始化并调用静态和非本地数据的构造函数,初始化线程本地存储,为该线程增加一个内部静态计数器每个附件,然后调用用户或库提供的DllMain
。
因为常规MFC DLL具有一个CWinApp
对象,所以它们应在与MFC应用程序相同的位置执行初始化和终止任务:在DLL 派生类的InitInstance
和ExitInstance
成员函数中CWinApp
。因为MFC提供了for 和DllMain
调用的函数,所以您不应编写自己的函数。MFC提供的函数在加载DLL时调用,并且在DLL卸载之前调用。_DllMainCRTStartup``DLL_PROCESS_ATTACH``DLL_PROCESS_DETACH``DllMain``DllMain``InitInstance``ExitInstance
参考链接:https://blog.csdn.net/nupt123456789/article/details/7343820
带静态链接MFC的规则的DLL
静态链接到MFC的规则DLL与MFC库(包括MFC扩展 DLL)静态链接,将MFC库的代码直接生成在.dll文件中。在调用这种DLL的接口时,MFC使用DLL的资源。因此,在静态链接到MFC 的规则DLL中不需要进行模块状态的切换。这种DLL是使用MFC的静态链接库版本构建的。函数通常使用标准C接口从常规MFC DLL中导出。(将DLL的相关代码写进EXE)
功能:
- 客户端可执行文件可以用任何支持DLL使用的语言编写,它不必是MFC应用程序。
- DLL可以链接到应用程序使用的同一MFC静态链接库。DLL的静态链接库不再有单独的版本。
- 在MFC的4.0版之前,USRDLL提供的功能类型与静态链接到MFC的常规MFC DLL相同。从Visual C ++版本4.0开始,术语USRDLL已过时。
要求
- 此类DLL必须实例化从派生的类
CWinApp
。 - 这种DLL使用
DllMain
MFC提供的。InitInstance
与ExitInstance
普通MFC应用程序一样,将所有特定于DLL的初始化代码放在成员函数中,并将终止代码放在其中。 - 即使术语USRDLL已过时,您仍然必须在编译器命令行上定义“ _USRDLL ”。此定义确定从MFC头文件中提取哪些声明。
常规MFC DLL中的所有内存分配都应保留在DLL中;DLL不应传递给调用可执行文件或从其接收以下任何内容:
- 指向MFC对象的指针
- 指向由MFC分配的内存的指针
使用共享MFC DLL的规则DLL
打包时MFC的DLL的内容没有被包含在EXE文件中,运行时要求系统中要有相关的DLL文件。(MFC: Microsoft Foundation Classes 可以理解为是一个工具,它是在C++环境下编写应用程序的一个框架和引擎。)
在动态链接到MFC的常规MFC DLL中所有导出功能的开头添加宏AFX_MANAGE_STATE(但是不能在常规MFC的DLL静态链接到MFC或MFC扩展DLL使用。),以将当前模块状态设置为DLL的状态。这是通过将以下代码行添加到从DLL导出的函数的开头来完成的:
AFX_MANAGE_STATE(AfxGetStaticModuleState( ))
动态链接到MFC的常规MFC DLL具有以下功能:
- 这是Visual C ++ 4.0引入的新型DLL。
- 客户端可执行文件可以用任何支持DLL使用的语言(C,C ++,Pascal,Visual Basic等)编写;它不必是MFC应用程序。
- 与静态链接的常规MFC DLL不同,这种类型的DLL是动态链接到MFC DLL(也称为共享MFC DLL)的。
- 链接到此类DLL的MFC导入库与MFC扩展DLL或使用MFC DLL的应用程序所使用的库相同:MFCxx(D).lib。
动态链接到MFC的常规MFC DLL具有以下要求:
- 这些DLL使用定义的**_AFXDLL进行编译,就像动态链接到MFC DLL的可执行文件一样。但是也定义了_USRDLL**,就像静态链接到MFC的常规MFC DLL一样。
- 这种类型的DLL必须实例化
CWinApp
派生的类。 - 这种DLL使用
DllMain
MFC提供的。InitInstance
与ExitInstance
普通MFC应用程序一样,将所有特定于DLL的初始化代码放在成员函数中,并将终止代码放在其中。
MFC扩展DLL
MFC扩展DLL的内涵为MFC的扩展,是使用MFC的动态链接库版本(也称为MFC的共享版本)构建的。只有使用MFC共享版本构建的MFC可执行文件(应用程序或常规MFC DLL)才可以使用MFC扩展DLL。
扩展DLL也可以用于在应用程序和DLL之间传递MFC派生的对象。与传递的对象关联的成员函数存在于创建对象的模块中。因为使用共享的DLL版本的MFC时这些函数已正确导出,所以您可以在应用程序与其加载的MFC扩展DLL之间*传递MFC或MFC派生的对象指针。
MFC扩展DLL具有以下功能和要求:
- 客户端可执行文件必须是使用
_AFXDLL
定义编译的MFC应用程序。 - 动态链接到MFC的常规MFC DLL也可以使用MFC扩展DLL。
- MFC扩展DLL应使用
_AFXEXT
定义进行编译。这_AFXDLL
也要进行定义,并确保从MFC头文件中提取正确的声明。它还确保将AFX_EXT_CLASS
其定义为__declspec(dllexport)
在构建DLL时的定义,如果使用此宏在MFC扩展DLL中声明类,则这是必需的。 - MFC扩展DLL不应实例化从派生的类
CWinApp
,而应依靠客户端应用程序(或DLL)来提供此对象。 - 但是,MFC扩展DLL应该提供一个
DllMain
功能并在那里进行任何必要的初始化。
MFC扩展DLL使用MFC的共享版本的方式与应用程序使用MFC的共享DLL版本的方式相同,但还有一些其他注意事项:
- 它没有
CWinApp
派生的对象。它必须与CWinApp
客户端应用程序的-derived对象一起使用。这意味着客户端应用程序拥有主消息泵,空闲循环等。 - 它调用
AfxInitExtensionModule
其DllMain
函数。应该检查该函数的返回值。如果从AfxInitExtensionModule
返回零值,则从DllMain
函数返回0 。 - 如果MFC扩展DLL想要将对象或资源导出到应用程序,则它将在初始化期间创建CDynLinkLibrary对象
CRuntimeClass
。
在MFC的4.0版之前,这种类型的DLL被称为AFXDLL。AFXDLL是指_AFXDLL
在构建DLL时定义的预处理器符号。
(本文大部分都是参考了微软官方文档)
本文作者:"( ̄y▽, ̄)╭ "
本文链接:https://blog.csdn.net/weixin_42703404/article/details/106065436
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
下一篇: RestTemplate发送HTTP请求
推荐阅读
-
解决Fedora14下eclipse进行android开发,ibus提示没有输入窗口的方法详解
-
CentOS7.0下安装PHP5.6.30服务的教程详解
-
win7-vs2012下安装.net frame work 的过程图文详解
-
详解Ubuntu Server下启动/停止/重启MySQL数据库的三种方式
-
vscode配置vue下的es6规范自动格式化详解
-
详解Spring Boot下Druid连接池的使用配置分析
-
详解docker下的Mysql镜像的使用方法
-
Linux下ps命令详解 Linux下ps命令的详细使用方法
-
Windows下获取Android 源码方法的详解
-
详解如何实现Element树形控件Tree在懒加载模式下的动态更新