文章目录
前言
动态库dll与静态库lib编程4:MFC规则DLL讲解。
一、说明
1.前面介绍的均为Win32DLL,即不使用MFC的DLL。
2.MFC规则DLL的特点:DLL内部可以使用MFC类库、可以被其他所有支持DLL技术的语言所调用(与Win32DLL一样)。
3.MFC规则DLL的入口点函数:默认情况下DLL的入口点函数都是DLLMain,MFC规则DLL也不例外,但是因为是支持MFC的,所以在MFC规则DLL中,DllMain函数已经被MFC所封装,所以在你的工程项目中是看不到DllMain函数的。就好像在MFC对话框工程中找不到WinMain函数是一样的,不过也有一些补救的技巧,就是InitInstance()和ExitInstance()函数,当进程初始化调用DLL时,DLL中会默认调用那个InitInstance()函数(在这个函数中写一些构造和初始化代码),当.exe退出时DLL会调用ExitInstance()函数(在这个函数中写一些退出和析构的代码),但是当有新的线程时,出于程序安全性考虑则没有什么好的办法进行处理。
4.MFC规则DLL分为两类:1.静态链接到MFC的规则DLL,与MFC库静态链接,会将MFC类库的代码直接编译生成到DLL文件中,在调用这种DLL的接口时,MFC使用DLL的资源,因此不需要模块状态的切换。但是缺点就是使用这种方式生成的DLL文件大小较大。2.动态链接到MFC的规则DLL:可以和调用DLL的.exe同时动态链接到MFC库,在这种情况下,MFC使用主应用程序(即:.exe程序)的资源句柄来加载资源模板,这样,当DLL和应用程序中存在相同ID的资源时,就要进行模块的切换,以便MFC能够找到正确的资源模板。
二、具体实现
2.1新建项目
先新建项目,选择MFC动态链接库,工程项目名字自定义即可,这里取名为MFCDLL,点击确定
这里为了演示模块状态的切换,使用动态链接到MFC的规则DLL,选择使用共享MFC DLL的常规DLL,这里不使用网络编程,附加功能不选择即可。
新建工程项目成功,编译器自动为我们生成了部分头文件和源文件
打开MFCDLL.cpp源文件,如上所述并没有入口点函数DllMain(),可以将入口点函数视为InitInstance()函数
源文件MFCDLL.cpp中并没有发现ExitInstance()函数,打开类视图,选择CMFCDLLAPP,打开属性,选择图示小立方体按钮
可以在上图中看到已经列出了ExitInstance()函数,选择添加ExitInstance()函数
则MFCDLL.cpp中已经自动出现了ExitInstance()函数框架,可以把析构和卸载方面的代码写在ExitInstance()函数中
2.2 模块切换的演示
在资源视图中右键MFCDLL.rc,点击添加资源
新建一个对话框资源
此时即在DLL工程中加入了一个对话框模板,将标题设置为DLL Dialog,自定义即可
在解决方案资源管理器中再新建一个工程,用于生成一个.exe调用生成的DLL
选择MFC应用,设置名字为MFCDllCall
应用程序类型选择对话框,其他默认即可,其他步骤都使用默认值,点击下一步,直到最后生成的类,点击完成
设置MFCDllCall项目为启动项目
同样在MFCDllCall项目中添加一个对话框模板
为了区分,将对话框的名字改为EXE Dialog
运行程序,此时主对话框默认为下图
我们进行一些更改,删去下图中框起部分
然后添加两个按钮
Button1用来调用exe的Dialog模板,Button2用来调用exe的Dialog模板,并重命名两个按钮,Button1的Caption设为EXE Dialog,ID设为IDC_EXE_BTN;Button2的Caption设为DLL Dialog,ID设为IDC_DLL_BTN。目的为当点击EXE Dialog时弹出工程MFCDllCall的对话框模板;点击DLL Dialog时弹出工程MFCDLL的对话框模板。
下面添加两个按钮的响应函数。这部分为MFC消息映射机制内容,具体讲解见https://blog.csdn.net/qq_59940419/article/details/144293369?spm=1001.2014.3001.5502,这里不再重复。
EXE Dialog按钮的响应函数如下,
cpp
void CMFCDllcallDlg::OnBnClickedExeBtn() // EXE Dialog按钮的响应函数
{
// TODO: 在此添加控件通知处理程序代码
CDialog dlg(IDD_DIALOG1); //定义一个变量 ,初始化为刚刚建立的对话框模板的ID(对应MFCDllCall项目的)
dlg.DoModal(); // 对话框的弹出
}
由于DLL工程需要利用一个函数导出,来实现DLL工程中对话框模板的显示,因此需要先实现如下代码来进行函数导出,这部分代码在MFCDLL.cpp源文件中实现。放在函数ExitInstance()的实现框架后即可
cpp
void ShowDLLDlg()
{
CDialog dlg(IDD_DIALOG1); //定义一个变量,初始化为刚刚建立的对话框模板的ID(对应MFCDLL项目的)
dlg.DoModal(); // 对话框的弹出
}
然后利用提供的* .def文件进行函数的导出
cpp
; MFCDLL.def: 声明 DLL 的模块参数。
LIBRARY "MFCDLL"
EXPORTS
; 此处可以是显式导出
ShowDLLDlg @1
编译后可以看到函数ShowDLLDlg()被成功导出
接下来需要在MFCDllcall工程中调用该导出函数ShowDLLDlg(),在MFCDllcallDlg.cpp源文件中添加如下代码进行函数的调用,放在文件上方系统自动生成的宏定义后即可
cpp
// 动态链接库导出函数的调用,利用隐式链接方法
#pragma comment(lib, "..\\DeBug\\MFCDLL.lib")
_declspec (dllimport) void ShowDLLDlg();
然后调用函数ShowDLLDlg()
cpp
void CMFCDllcallDlg::OnBnClickedDllBtn() // DLL Dialog按钮的响应函数
{
// TODO: 在此添加控件通知处理程序代码
ShowDLLDlg();
}
打开MFCDllcall工程的Resource.h头文件,查看IDD_DIALOG1对应的数值定义为129(每个人可能不一样)
打开MFCDLL工程的Resource.h头文件,查看IDD_DIALOG1对应的数值定义为1000(每个人可能不一样)
将MFCDLL工程的Resource.h头文件中IDD_DIALOG1对应的数值也定义为和MFCDllcall工程一样的129。如果不进行更改点击DLL Dialog按钮是没有作用的,二者需保持一致。
这样就会出现一种错误现象,运行程序,点击EXE Dialog按钮,弹出EXE Dialog对话框模板;
点击DLL Dialog按钮,希望弹出的是DLL Dialog对话框模板,但是也弹出EXE Dialog对话框模板,这就出现问题了
造成这种现象的原因是:动态链接到MFC的规则DLL默认情况下会使用主应用程序(.exe)的资源句柄来加载资源模板。所以这里就使用MFCDllcall工程中的对话框模板。解决的办法是模块状态切换,即在执行MFCDLL工程内部代码的时候,需要调用该工程的内部资源;在执行MFCDllcall工程内部代码的时候,需要调用该工程的内部资源。
解决方法共有三种:
1.AFX_MANAGE_STATE(AfxGetStaticModuleState());放入函数ShowDLLDlg()的实现中
cpp
void ShowDLLDlg()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 将资源的主句柄转换到动态链接库当中,只要程序中加载资源,都在该程序所在工程中进行查找
CDialog dlg(IDD_DIALOG1); //定义一个变量,初始化为刚刚建立的对话框模板的ID(对应MFCDLL项目的)
dlg.DoModal(); // 对话框的弹出
}
此时点击DLL Dialog按钮弹出对话框正确,说明资源模块切换成功。
- 意思和方法1是一样的,代码相对复杂
cpp
HINSTANCE hSaveInst = AfxGetResourceHandle();
AfxSetResourceHandle(theApp.m_hInstance);
//...执行代码
AfxSetResourceHandle(hSaveInst);
此时的ShowDLLDlg()函数实现如下:
cpp
void ShowDLLDlg()
{
// 方法1:
//AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 方法1:将资源的主句柄转换到动态链接库当中,只要程序中加载资源,都在该程序所在工程中进行查找。
// 方法2:
HINSTANCE hSaveInst = AfxGetResourceHandle();// 方法2:取得当前应用程序的实例句柄,保存到变量hSaveInst,此时实例句柄为.exe工程的
AfxSetResourceHandle(theApp.m_hInstance);// 方法2:将当前的实例句柄设置为DLL的,之后实例句柄为DLL工程的
CDialog dlg(IDD_DIALOG1); //定义一个变量,初始化为刚刚建立的对话框模板的ID(对应MFCDLL项目的)
dlg.DoModal(); // 对话框的弹出
AfxSetResourceHandle(hSaveInst);// 方法2:将实例句柄设置回来.exe的
}
3.前两种方法都是在MFCDLL工程中实现函数ShowDLLDlg()中进行,第三种方法不同,是在MFCDllcall工程,即.exe工程中进行修改。
先将方法1和方法2的代码注释掉
在MFCDllcallDlg.cpp源文件中找到调用函数ShowDLLDlg()部分
修改后变为如下代码:
cpp
void CMFCDllcallDlg::OnBnClickedDllBtn() // DLL Dialog按钮的响应函数
{
// TODO: 在此添加控件通知处理程序代码
// 资源模块切换方法3:
HINSTANCE hExeInst = GetModuleHandle(NULL); //资源模块切换方法3: 取得指定模块的句柄,参数为模块的路径,返回传入路径文件的实例句柄,参数为NULL返回当前exe的实例句柄;NULL可换为_T("MFCDllcall.exe")
HINSTANCE hDLLInst = GetModuleHandle(_T("MFCDLL.dll")); //资源模块切换方法3: 注意:该路径为与MFCDllcall.exe的相对路径
ASSERT(hExeInst && hDLLInst); //资源模块切换方法3: 判断一下这两个句柄都不为空
AfxSetResourceHandle(hDLLInst); //资源模块切换方法3: 资源搜索句柄设为动态链接库的
ShowDLLDlg();
AfxSetResourceHandle(hExeInst); // 资源模块切换方法3:将资源搜索句柄变回来.exe的
}
实际上,最方便的还是第一种方法,前两种方法是在DLL导出函数中添加,第三种方法是在.exe程序中进行添加。
总结
动态库dll与静态库lib编程4:MFC规则DLL讲解。