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

文章目录

  • 前言
  • 一、说明
  • 二、具体实现
    • 2.1新建项目
    • 2.2 模块切换的演示
  • 总结


前言

动态库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按钮的响应函数如下,

void CMFCDllcallDlg::OnBnClickedExeBtn() // EXE Dialog按钮的响应函数
{// TODO: 在此添加控件通知处理程序代码CDialog dlg(IDD_DIALOG1); //定义一个变量 ,初始化为刚刚建立的对话框模板的ID(对应MFCDllCall项目的)dlg.DoModal(); // 对话框的弹出
}

由于DLL工程需要利用一个函数导出,来实现DLL工程中对话框模板的显示,因此需要先实现如下代码来进行函数导出,这部分代码在MFCDLL.cpp源文件中实现。放在函数ExitInstance()的实现框架后即可

void ShowDLLDlg()
{CDialog dlg(IDD_DIALOG1); //定义一个变量,初始化为刚刚建立的对话框模板的ID(对应MFCDLL项目的)dlg.DoModal(); // 对话框的弹出
}

然后利用提供的* .def文件进行函数的导出

; MFCDLL.def: 声明 DLL 的模块参数。LIBRARY "MFCDLL"EXPORTS; 此处可以是显式导出ShowDLLDlg @1

编译后可以看到函数ShowDLLDlg()被成功导出
在这里插入图片描述
接下来需要在MFCDllcall工程中调用该导出函数ShowDLLDlg(),在MFCDllcallDlg.cpp源文件中添加如下代码进行函数的调用,放在文件上方系统自动生成的宏定义后即可

// 动态链接库导出函数的调用,利用隐式链接方法
#pragma comment(lib, "..\\DeBug\\MFCDLL.lib")
_declspec (dllimport) void ShowDLLDlg();

然后调用函数ShowDLLDlg()

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()的实现中

void ShowDLLDlg()
{AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 将资源的主句柄转换到动态链接库当中,只要程序中加载资源,都在该程序所在工程中进行查找CDialog dlg(IDD_DIALOG1); //定义一个变量,初始化为刚刚建立的对话框模板的ID(对应MFCDLL项目的)dlg.DoModal(); // 对话框的弹出
}

此时点击DLL Dialog按钮弹出对话框正确,说明资源模块切换成功。
在这里插入图片描述
2. 意思和方法1是一样的,代码相对复杂

HINSTANCE hSaveInst = AfxGetResourceHandle();
AfxSetResourceHandle(theApp.m_hInstance);
//...执行代码
AfxSetResourceHandle(hSaveInst);

此时的ShowDLLDlg()函数实现如下:

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()部分
在这里插入图片描述
修改后变为如下代码:

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讲解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/502304.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

若依中Feign调用的具体使用(若依微服务版自身已集成openfeign依赖,并在此基础上定义了自己的注解)

若依中Feign调用具体使用 注意:以下所有步骤实现的前提是需要在启动类上加入注解 EnableRyFeignClients 主要是为开启feign接口扫描 1.创建服务提供者(provider) 导入依赖(我在分析依赖时发现若依本身已经引入openfeign依赖,并在此基础上自定义了自己的EnableRyF…

CSS3——3. 书写格式二

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title></head><body><!--css书写&#xff1a;--><!--1. 属性名:属性值--><!--2.属性值是对属性的相关描述--><!--3.属性名必须是…

zookeeper 数据类型

文章目录 引言I Znodezonde stat (状态信息)znode类型临时\永久序列化特性引言 在结构上与标准文件系统非常类似,拥有一个层次的命名空间,都是采用树形层次结构 Zookeeper树中的每个节点被称为:Znode,没有文件和目录之分。Znode兼具文件和目录两种特点Znode存储数据大小有…

Hadoop集群之间实现免密登录

实现虚拟机之间能够互相登录&#xff0c;比如可以在hadoop1上面登录hadoop2。 第一步&#xff1a;执行”ssh-keygen -t rsa”命令&#xff0c;生成该虚拟机的密钥 第二步&#xff1a;密钥文件存储在/root/.ssh目录&#xff0c;执行cd /root/.ssh命令进入存储密钥文件的目录&am…

【linux基础I/O(1)】文件描述符的本质重定向的本质

目录 前言1. 理解C语言的文件接口2. 操作文件的系统调用接口2.1 open函数详解2.2 close函数详解2.3 write函数详解2.4 read函数详解 3. 文件描述符fd详解4. 文件描述符的内核本质5. 怎样理解Linux下一切皆文件?6. 理解输出输入重定向7. 重定向的系统调用8. 总结 前言 “在Lin…

C++:范围for

范围for&#xff08;range-based for&#xff09;是C的一种循环结构&#xff0c; 是在 C11 这个标准中引入的&#xff0c;这种类型的for循环使得遍历数组、容器中的元素更加简便和直观。 一、范围for语法 for ( 类型 变量名 : 数组名 ) 语句 //多条语句需要加⼤括号 示例&#…

C语言 递归编程练习

1.将参数字符串中的字符反向排列&#xff0c;不是逆序打印。 要求&#xff1a;不能使用C函数库中的字符串操作函数。 比如&#xff1a; char arr[] "abcdef"; 逆序之后数组的内容变成&#xff1a;fedcba 1.非函数实现&#xff08;循环&#xff09; 2.用递归方法…

Spring Boot - 日志功能深度解析与实践指南

文章目录 概述1. Spring Boot 日志功能概述2. 默认日志框架&#xff1a;LogbackLogback 的核心组件Logback 的配置文件 3. 日志级别及其配置配置日志级别3.1 配置文件3.2 环境变量3.3 命令行参数 4. 日志格式自定义自定义日志格式 5. 日志文件输出6. 日志归档与清理7. 自定义日…

USB子系统学习(一)USB电气信号

文章目录 1、声明2、USB协议概述3、USB电气信号3.1、USB基础概念3.1.1、低速/全速信号电平3.1.2、高速信号电平 3.2、学习目标3.3、设备断开与连接3.3.1、连接3.3.2、断开 3.4、复位3.5、设备速率识别3.5.1、低速/全速3.5.2、高速 3.6、数据信号3.6.1、低速/全速的SOP和EOP3.6.…

Android GameActivity(NativeActivity)读写文件

最近研究native android相关内容&#xff0c;其中最棘手的就是文件读写问题&#xff0c;最主要的是相关的文档很少。这里写下我所知道的方法。 由于本人使用的是Android14[arm64-v8a]版本的设备,能访问的路径相当有限&#xff0c;如果想要访问更多的路径&#xff0c;就不得不申…

安卓入门十一 常用网络协议四

MQTT&#xff08;Message Queuing Telemetry Transport&#xff09; MQTT是一种轻量级的、发布/订阅模式的消息传输协议。它被设计用于在低带宽或不稳定网络环境下&#xff0c;实现物联网设备之间的可靠通信。 4.1 MQTT详细介绍 发布/订阅模式&#xff1a;MQTT 使用发布/订…

气膜球幕:引领元宇宙时代的科技与艺术光影盛宴—轻空间

在科技与艺术交织的时代&#xff0c;未来的观影体验将不再受限于传统屏幕的束缚。随着气膜球幕的崭新亮相&#xff0c;突破性的光影效果和沉浸式体验让我们走进了一个全新的视听世界。这不仅仅是一个简单的球形影院&#xff0c;它是连接现实与虚拟、科技与艺术、光与影的桥梁&a…

Hyperbolic dynamics

http://www.scholarpedia.org/article/Hyperbolic_dynamics#:~:textAmong%20smooth%20dynamical%20systems%2C%20hyperbolic%20dynamics%20is%20characterized,semilocal%20or%20even%20global%20information%20about%20the%20dynamics. 什么是双曲动力系统&#xff1f; A hy…

kernel32.dll动态链接库报错要怎解决?详细解析kernel32.dll文件缺失解决方案

Kernel32.dll动态链接库报错详解与解决方案 在电脑的日常使用中&#xff0c;我们时常会遇到各种系统报错&#xff0c;其中kernel32.dll文件的报错尤为让人头疼。作为一名在软件开发领域摸爬滚打多年的从业者&#xff0c;我将为大家深入解析kernel32.dll文件的重要性&#xff0…

win10 npm login 登陆失败

npm login 命令总是登陆失败 提示我们设置带proxy。我们本地找到这个 找到代理地址 进行关键信息的设置 npm config set proxy http://xxxx:xxxx npm config set https-proxy http://xxx:xxx 设置完之后在执行npm login即可

[Qt] 输入控件 | Line | Text | Combo | Spin | Date | Dial | Slider

目录 输入类控件 1、Line Edit 录入个人信息 使用正则表达式验证输入框的数据 验证两次输入的密码一致 切换显示密码 2、Text Edit 获取多行输入框的内容 验证输入框的各种信号 3、Combo Box 使用下拉框模拟麦当劳点餐 从文件中加载下拉框的选项 4、Spin Box 调整…

SpringCloud源码-Ribbon

一、Spring定制化RestTemplate&#xff0c;预留出RestTemplate定制化扩展点 org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration 二、Ribbon定义RestTemplate Ribbon扩展点功能 org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguratio…

基于单片机的数字电子秒表设计

此文章谨为课设记录 一、实验要求 题目六 数字电子时钟 基本要求&#xff1a; (1) 设计一个单片机电子时钟&#xff0c;设计的电子时钟通过数码管显示&#xff1b; (2) 具有能通过按键实现设置时间的功能&#xff1b; (3) 显示格式为小时十位、小时个位&#xff0c;分…

【业务场景】sql server从Windows迁移到Linux

目录 1.背景 2.Linux安装sql server 3.服务器不开端口的问题 4.数据库导入导出问题 1.背景 博主在24年年底接手运维了一个政府的老系统&#xff0c;整个应用和数据库单点部署在一台Windows Server服务器上&#xff0c;数据库选型是经典的老项目标配——sql server。随着近…

Flink CDC 自定义函数处理 SQLServer XML类型数据 映射 doris json字段方案

Flink CDC 自定义函数处理 SQLServer XML类型数据方案 1. 背景 因业务使用SQLServer数据库&#xff0c;CDC同步到doris 数仓。对于SQLServer xml类型&#xff0c;doris没有相应的字段对应&#xff0c; 可以使用json来存储xml数据。需要进行一步转换。从 flink 自定义函数入手…