动态库dll与静态库lib编程4:MFC规则DLL讲解

文章目录


前言

动态库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. 意思和方法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讲解。

相关推荐
Linux520小飞鱼32 分钟前
F#语言的网络编程
开发语言·后端·golang
weixin_3992642937 分钟前
QT c++ 样式 设置 标签(QLabel)的渐变色美化
开发语言·c++·qt
吾当每日三饮五升4 小时前
C++单例模式跨DLL调用问题梳理
开发语言·c++·单例模式
猫武士水星4 小时前
C++ scanf
开发语言·c++
BinaryBardC5 小时前
Bash语言的数据类型
开发语言·后端·golang
Lang_xi_5 小时前
Bash Shell的操作环境
linux·开发语言·bash
Pandaconda5 小时前
【Golang 面试题】每日 3 题(二十一)
开发语言·笔记·后端·面试·职场和发展·golang·go
捕鲸叉5 小时前
QT自定义工具条渐变背景颜色一例
开发语言·前端·c++·qt
想要入门的程序猿5 小时前
Qt菜单栏、工具栏、状态栏(右键)
开发语言·数据库·qt
Rossy Yan6 小时前
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
c++·排序算法·面向对象·封装·查找