前面章节,我们介绍了如何在 Windows 和 Linux 平台上创建和使用静态链接库。本节将围绕动态链接库(Dynamic Link Library,简称 DLL),研究在 Windows 和 Linux 平台上创建和使用它的详细过程。
首先,我们了解一下 Windows 上是如何创建和使用动态链接库的。
Windows 平台上,创建动态链接库文件(.dll)的同时还必须生成一个引入库文件(.lib)。
我们知道,.lib 是静态库文件的后缀名,虽然引入库文件的后缀名也是 .lib,但两者有着本质上的区别。引入库文件和动态链接库搭配使用,动态链接库负责存储定义好的函数和变量,引入库负责记录动态库中哪些函数和变量允许被外界调用,记录的信息包含函数和变量的名称以及它们在动态库中的存储位置。
引入库文件的作用是“告诉”静态链接器哪些函数或者变量的定义位于动态库中,将这些函数和变量的链接工作向后推迟,等到程序执行时再进行(由动态链接库完成)。
那么,到底怎样生成引入库文件和动态库文件呢?接下来我们用 VS2019 将前面章节中的 myMath.c 库文件转换为动态链接库,整个过程分为以下几步:
1) 创建一个动态链接库的项目,打开 VS 2019,选择“创建新项目”:
弹出的对话框中,选择创建“动态链接库(DLL)”项目,然后点击“下一步”:
在图 3 所示的对话框中,自定义项目名称和存储位置,其它选项保持默认即可:
点击“创建”按钮后,动态链接库的项目就创建完成了,初始状态下的项目如图 4 所示:
2) 图 4 中所示的这些文件,是 VS 默认生成的,其中最重要的是 dllmain.cpp 文件,包含以下内容:
如同 DOS 程序需要 main() 作为入口函数一样,Windows 系统加载动态链接库时也需要一个入口函数,DllMain() 函数就是动态链接库的入口函数。有些动态链接库文件中没有提供 DllMain() 函数也可以正常载入,因为系统会引入一个默认的、不具有任何操作的 DllMain() 函数。
DllMain() 函数中,hModule 表示本 DLL 程序的句柄,ul_reasion_for_call 用于指定动态链接库被调用的原因,lpReserved 是一个保留参数,目前很少使用。
ul_reasion_for_cal 参数的值有 4 种,代表了 4 种调用 DLL 程序的场景,分别是:
3) 我们的初衷是将 myMath.c 库文件转换为动态链接库,图 4 中的这些文件可以全部删除。由于项目中没有使用 VS 提供的 pch.h 等文件,需要修改一下 VS 的设置,具体做法是:鼠标右击图 4 中的项目名,选择 "属性",在图 5 所示的对话框中完成如下修改操作:
删除原有的所有文件后,在项目中添加 myMath.h 和 myMath.c 两个文件,它们各自包含的代码为:
//myMath.h
//实现两个整数相加,返回它们的和
__declspec(dllexport) int add(int a, int b);
//实现两个整数相减,返回它们的差
__declspec(dllexport) int sub(int a, int b);
//实现两个整数相乘,返回它们的乘积
__declspec(dllexport) int mul(int a, int b);
//实现两个整数相除,返回它们的商
__declspec(dllexport) int div(int a, int b);
//myMath.c
#include "myMath.h"
int add(int a, int b) {
return a + b;
}
int sub(int a, int b) {
return a - b;
}
int mul(int a, int b) {
return a * b;
}
int div(int a, int b) {
if (b != 0) {
return a / b;
}
return -1;
}
注意在 Windows 平台上,动态链接库中的函数或者变量要想被外界调用,必须用 __declspec(dllexport) 修饰。myMath.h 头文件中,我们分别用 __declspec(dllexport) 修饰了 4 个函数,最终生成的动态链接库就提供有 4 个函数供其它程序调用。
除了用 __declspec(dllexport) 注明被外界调用的函数和变量外,还可以借助 .def 文件指明允许被外界调用的函数和变量,这里不再展开讲解,感兴趣的读者可自行查阅相关资料。
4) 鼠标右键点击图 4 所示的项目名 myMath,选择“重新生成”,如果 VS 的执行结果如图 6 所示,则表明成功生成动态链接库。
我们可以轻松找到生成的动态链接库的存储位置,此外在同路径下还生成了一个名为 myDLL.lib 的文件,这就是引入库文件。
接下来,我们以刚刚生成的 myDLL.dll 动态链接库为例,教大家如何在自己的项目中使用他人分享的动态链接库。
首先,分享动态链接库时需要准备 3 个文件,一个是动态链接库的头文件,方便用户了解动态链接库有哪些功能可以用,还有就是动态链接库文件和引入库文件。分享头文件之前,我们还需要将其内部的所有 __declspec(dllexport) 改为 __declspec(dllimport),经过修改后的头文件才能被用户使用。
__declspec(dllexport) 和 __declspec(dllimport) 搭配使用,前者用于生成动态链接库时指明哪些函数和变量可以被外界调用,后者的作用是告知用户和编译器哪些函数和变量的定义位于动态链接库中。
假设在 MyDemo 项目中使用 myDLL.dll 动态链接库,此项目只有一个 main.c 文件,包含的代码为:
#include <stdio.h>
int main() {
int a = 3, b = 4;
printf("a+b=%d\n", add(a, b));
printf("a-b=%d\n", sub(a, b));
printf("a*b=%d\n", mul(a, b));
printf("a/b=%d", div(a, b));
return 0;
}
将 myDLL.dll 载入到 MyDemo 项目的过程分为以下几步:
1) 将修改过的 myMath.h 以及 myDLL.lib 和 myDLL.dll 文件拷贝至 MyDemo 项目中,MyDemo 项目的存储位置可以通过 "右击资源管理器中的项目名 -> 在资源管理器中打开文件夹" 找到。
2) 将 myMath.h 头文件和 myDLL.lib 引入到 myDemo 项目中,引入后的 MyDemo 如图 7 所示:
3) main.c 文件中添加#include "myMath.h"语句后,运行 MyDemo 项目,执行结果为:
显然,我们已经成功地将动态链接库引入到了项目中。