- 背景介绍
DLL(动态链接库)的封装在软件开发中有许多重要的目的和优势,包括代码复用、模块化设计、内存管理等。以下是进行DLL封装的主要原因和优势:
- 代码复用,多个应用程序可以共享同一个DLL,从而避免重复代码。例如,一个通用的图像处理库可以封装成DLL,供多个应用程序调用。
- 容易维护和更新,通过DLL封装,共享库的更新和维护变得更加容易。更新DLL文件就可以让所有使用它的应用程序受益,而不需要分别更新每个应用程序。
- 分离功能,将不同功能模块封装到不同的DLL中,使得代码更加模块化和结构化。这种分离有助于管理大型项目,分工协作更加清晰。
- 降低耦合,通过DLL,应用程序和库之间的依赖关系变得松散。这样,当某个模块需要修改时,不会影响整个系统,从而提高了系统的灵活性和可维护性。
- 内存和资源管理,由于多个应用程序可以共享同一个DLL实例,操作系统可以更高效地管理内存。多个程序使用同一个DLL时,DLL只会加载一次,节省了内存空间。
- 动态加载和卸载,应用程序可以在需要时动态加载DLL,并在不需要时卸载,从而更高效地管理系统资源。这样可以显著减少内存占用和启动时间。
- 支持多语言开发,DLL可以被不同编程语言开发的程序调用。比如,用C++编写的DLL可以被C#、Python、Delphi等语言调用,这为开发跨语言应用提供了极大的便利。
- 安全性和保护知识产权,通过DLL封装,可以隐藏实现细节,只暴露必要的接口函数。这有助于保护知识产权,防止核心算法和实现细节被直接查看和复制。还可以通过DLL进行更细粒度的权限控制,限制某些功能的访问权限。
- 增强应用程序的灵活性,通过DLL,可以实现插件机制,使应用程序可以通过加载不同的DLL来扩展功能,而不需要修改主程序。这种机制常用于浏览器、媒体播放器和各种编辑器等应用中。并且可以通过不同版本的DLL文件实现不同功能或修复bug,而不需要重新编译主程序。
本次使用的方式是基于MFC框架封装DLL的demo。下面简单介绍MFC的基本背景以及其工作原理。
Microsoft Foundation Class (MFC) 是一个面向对象的C++类库,用于简化Windows应用程序的开发。MFC 最早在 1992 年作为 Microsoft C/C++ 7.0 的一部分发布,是微软为简化 Windows 程序开发而设计的一个 C++ 类库。它提供了一组面向对象的类,封装了 Windows API,极大地简化了 Windows 应用程序的开发。随着 Visual Studio 的每一代更新,MFC 也不断发展,增加了对新的 Windows 功能和技术的支持。
MFC提供了一个抽象层,封装了Windows API,使开发者能够更高效地创建Windows应用程序。下面是MFC的基本原理:
- 消息映射机制: MFC使用消息映射机制将Windows消息(如用户输入、窗口事件等)与相应的成员函数关联起来。这通过宏 BEGIN_MESSAGE_MAP 和 END_MESSAGE_MAP 以及 ON_WM_* 等宏实现。
- 文档/视图架构: MFC支持文档/视图架构(Document/View Architecture),将应用程序的数据逻辑(文档)与用户界面(视图)分离。文档类(通常继承自 CDocument)处理数据存储和业务逻辑,视图类(通常继承自 CView)负责数据显示和用户交互。
- 应用程序框架: MFC提供了一个完整的应用程序框架,包括应用程序类(继承自 CWinApp)、主窗口类(继承自 CFrameWnd)以及各种控件和对话框类。
- 安装MFC配置
Visual studio2019中默认不会安装MFC,我们使用时需要提前安装:
找到Visual studio2019的安装程序installer并打开,选择修改选项,进入安装界面如下图:
勾选c++的桌面开发并在右侧选上C++MFC for V142生成工具:
点击修改并等待安装完毕:
- 创建DLL
(一)如图,选好要创建的项目
取个名字,选好地址,点击“创建”
在解决方案资源管理器中,右击“头文件”——>"添加“——>"新建项",
选择”头文件(.h)"——>取个名字——>点击“添加”
封装一个接口类,类里面申明一个add函数:
#pragma once
#include <iostream>
using namespace std;
class _declspec(dllexport) PATH;
class PATH
{
public:
PATH(); //构造函数
int add(int a, int b);
};
在解决方案资源管理器中,右击“源文件”——>"添加“——>"新建项",并选择”C++文件(.cpp)"——>取个名字——>点击“添加”
定义类中的函数:
#include "pch.h"
#include "dlltest.h" //把刚刚的头文件包含进去
PATH::PATH()
{
}
int PATH::add(int a, int b)
{
return a + b;
}
生成DLL:法一:”ctrl+B" \
法二:在菜单栏找到“生成”——>“生成解决方案”
可以看到Debug文件夹下,有这些文件生成:
- 创建MFC应用项目调用DLL
如下图所示,选择创建MFC应用:
创建项目:
创建出来是这个样子:
参照前面添加头文件的方法(右击“头文件”——>"添加“——>"新建项"——>"头文件”),添加一个头文件(注意:头文件的名称要与DLL中的“dlltest.h”头文件名称保持一致)
新添加的头文件名字要与dll中的对应。
将前面DLL的头文件“dlltest.h”的内容全部复制,粘贴到MFC项目中的“dlltest.h”头文件中:
#pragma once
#include <iostream>
using namespace std;
class _declspec(dllimport) PATH;
class PATH
{
public:
PATH(); //构造函数
int add(int a, int b);
};
注意,一定要把“dllexport"改成”dllimport“
接着,修改MFC项目的属性,指向DLL的文件夹(生成.dll和.lib的文件夹)。
菜单栏”项目“——>"属性”——> “配置属性”——>" VC++目录"——>"库目录"——>点击库目录右侧的“”按钮(如图所画位置)——>"编辑"——>点击“”——>将路径指向DLL的Debug文件夹(生成.dll和.lib的文件夹)——>"确定“
接下来准备调用:
首先,要将DLL的头文件包含进去,再用“#pragma comment()”引入刚刚的dll库:
#include "dlltest.h"
#pragma comment(lib,"DllTest.lib")
其次,将前面DLL项目生成的.dll文件赋值到MFC项目文件夹底下
最后,新建一个方法类,编写作调用DLL中的类以及类中的函数:
void CexetestDlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
PATH P;
int m = P.add(1, 3);
//MessageBox没法直接显示int类型变量m,所以先将int转换成CString类型
CString str;
str.Format(_T("%d"), m);
MessageBox(str);
}
运行代码:
执行成功!