一、说明
动态链接库(Dynamic Link Library 或者 Dynamic-link Library,缩写为 DLL),是微软公司在微软Windows操作系统中,实现共享函数库概念的一种方式。
VS2019 提供了三种创建动态库的模式,如下图所示:
二、编写动态库
2.1 创建动态库
选择“具有导出项的(DLL)动态链接库”,vs会帮我们自动创建与项目同名的.cpp文件和.h文件,并在.h文件中定义好相关导出符号;如果选择“动态链接库(DLL)”则不会创建上述文件。
直到出现下图为止:
2.2 普通动态库封装
新建一个动态库工程,将其命名为Dll1,创建一个cpp文件,一个.h文件
结构如下:
说明一下extern “C” __declspec(dllexport) int Add(int a, int b);这一句代码
- 其中extern "C"的作用是告诉编译器将被它修饰的代码按C语言的方式进行编译
- __declspec(dllexport),此修饰符告诉编译器和链接器被它修饰的函数或变量需要从DLL导出,以供其他应用程序使用;
- 与__declspec(dllexport)相对的还有一句代码是__declspec(dllimport),此修饰符的作用是告诉编译器和链接器被它修饰的函数或变量需要从DLL导入。
2.3 将类封装为动态库
新建一个动态库工程,将其命名为Dll2,创建一个ClassTest的类,程序结构如图所示
三、动态库调用
上述代码直接生成,即可得到.lib和.dll文件
3.1 动态库的静态调用
新建一个空工程,复制上述两个程序的.h文件,将其导入,程序结构如图所示:
修改两个文件的.h文件
- 将__declspec(dllexport)替换为__declspec(dllimport)
- 加入#pragma comment(lib,"…/Debug/Dll2.lib"),此代码用来静态导入
说明:
不替换为 __declspec(dllimport)也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。
新建main函数,测试普通函数动态库的调用:
测试普通函数和类封装成的动态库的调用:
3.2 普通动态库的动态调用
新建空工程,新建main.cpp加入以下代码,运行即可
3.3 类封装成动态库的动态调用
新建动态库,命名为Dll4
新建base类,将其写为虚类,此类作为导出类:
新建classTest3,此类继承base
classTest3.cpp文件中极爱如导出句柄的函数:
此时Dll4 的结构如图所示
新建空白工程,复制base.h,创建main.cpp,程序结构如图所示:
修改base.h
main.cpp
#include <iostream>
#include <windows.h>
#include "Base.h"
using namespace std;
void Func1() {
typedef int(*lpFunc)(int a, int b);
HMODULE hModule;
hModule = LoadLibrary(L"Dll1.dll"); //调用DLL
lpFunc Add = NULL;
Add = (lpFunc)GetProcAddress(hModule, "Add");
cout << Add(3, 5) << endl;
}
void Func2() {
typedef Base* (*pfGetInst)();
HMODULE hMod = LoadLibrary(L"Dll4.DLL");
if (hMod)
{
pfGetInst pfGetInstance = (pfGetInst)GetProcAddress(hMod, "GetInstance");
if (pfGetInstance)
{
//通过基类指针指向派生类对象
Base* pInst = pfGetInstance();
if (NULL != pInst)
{
cout << pInst->Add(1, 2) << endl;
}
if (NULL != pInst)
{
//释放对象
delete pInst;
}
}
}
}
int main()
{
Func1();
Func2();
cin.ignore();
return 0;
}
运行效果如下图所示: