一、动态链接库和动态链接
动态链接库:在 Windows 中,动态链接库 (DLL) 是作为函数和资源的共享库的一种可执行文件。在调用它们的应用程序的上下文中运行。
动态链接:动态链接是操作系统功能。操作系统将 DLL 加载到应用程序的内存空间中,它可使执行文件调用函数或使用存储在单独文件中的资源。
动态链接库有什么用: DLL 可以在可执行文件之间轻松共享函数和资源。 多个应用程序可同时访问内存中单个 DLL 副本的内容。
二、和静态链接的区别
这部分我们只需要知道静态链接把所有的执行需要的库都打包到可执行文件里面了,动态链接则是把各个库独立出来,在运行时由操作系统将 DLL 加载到应用程序的内存空间中。
在Windows上具体一些就是:
静态链接将静态库中的所有对象代码复制到生成时使用它的可执行文件中。 动态链接仅包括 Windows 在运行时用于查找和加载含有数据项或函数的 DLL 所需的信息。 创建 DLL 时,还将创建包含此信息的导入库。 生成调用 DLL 的可执行文件时,链接器会使用导入库中的导出符号来为 Windows 加载程序存储此信息。 当加载程序加载 DLL 时,该 DLL 会映射到你的应用程序的内存空间中。 如果存在,则调用 DLL 中的特殊函数 DllMain,以执行 DLL 所需的任何初始化。
也可以直接看看微软技术文档的介绍,单击蓝字访问。
三、用VS生成动态库的基本流程
这里我们使用C++语言开发,对于环境有的要求如下:
1、Visual Studio。
2、VS中安装了 “C++ 的桌面开发” 工作负载。 如果在安装 Visual Studio 时未安装此工作负载, 可以再次运行安装程序并立即安装。
Visual Studio的安装和配置可以参考这个链接,单击蓝字访问即可。
具体步骤如下:
①在 Visual Studio 2019 中创建 DLL 项目
在菜单栏上,选择“文件”>“新建”>“项目”,打开“创建新项目”对话框 。选择如下图所示的项目类型。
接下来,创建一个头文件来声明 DLL 导出的函数,然后将函数定义添加到 DLL,使其具备更强大的功能(被其他执行文件调用)。
②将头文件添加到 DLL(.h文件)
若要为函数创建头文件,请在菜单栏上选择“项目” > “添加新项” 。也就是往这个项目里面添加一个头文件,大伙应该都知道咋个弄。
图例添加了一个名为MathLibrary.h
的头文件,这里随意用啥名儿都行,主要看操作步骤。我们统一设定为DiyApi.h
然后把头文件DiyApi.h
的内容写成这样:
#pragma once //声明一个作为API的函数 //并对函数GetNumber_8()声明设置 __declspec(dllexport) 修饰符。 extern "C" __declspec(dllexport) int GetNumber_8();
这里的函数GetNumber_8()
将会作为我们这个DLL的一个API,用于被其它执行文件调用。
__declspec(dllexport)
指示编译器和链接器从 DLL
导出函数或变量,以便其他应用程序可以使用它。此修饰符可优化应用程序中函数或变量的导入。可以参阅微软的说明文档,单击蓝字即可查看。
③向 DLL 添加实现(.c文件)
还是强调过程,我们把函数的功能搞得简单点:
#include "DiyApi.h" #include <stdio.h> /** brief:返回int类型的数字8 arg:None retval:8 **/ int GetNumber_8() { int ConstNumber = 8; return ConstNumber; }
现在可以编译动态链接库,DLL
和相关编译器输出放在解决方案文件夹正下方的“Debug
”文件夹中 。 如果创建发布版本(Realse
),该输出会放置在“Release
”文件夹中 。 VS的输出应类似于:
四、如何使用动态链接库
显然,不管是我们自己写的动态库(DLL
)还是其他来源的动态库,首先要考虑的问题就是怎么把它用到我们的项目(Project)里。这也就是如何使用动态链接库的问题。
微软文档指出:
若要调用函数或访问由 DLL 导出的数据,客户端源代码必须在编译时具有可用的声明。 在链接时间,链接器需要信息来解析函数调用或数据访问。
也就是说,至少确保两件事情:
① IDE知道动态库的位置,以便编译的顺利进行
② 我们发布的应用程序能够找到动态库,以确保程序能够运行
所以我们一般应该遵循以下原则:
首先,为避免不同步的代码,建议在客户端项目中设置包含路径,使其直接包括 DLL 项目中的 DLL 头文件。
此外,在客户端项目中设置库路径以包括 DLL 项目中的 DLL 导入库。
最后,将生成的 DLL 从 DLL 项目复制到客户端生成输出目录中。 此步骤允许客户端应用使用生成的同一 DLL 代码。
为了清晰地说明这个问题,我们走一遍流程。首先是新建一个工程ApiClient
,我们将在这个工程中调用先前编写的DLL
中的GetNumber_8()
.
添加DLL到工程(编译准备)
接下来,要在源代码中调用 GetNumber_8
函数,你的项目必须包括 DiyApi.h
文件 。为避免不必要的麻烦,我们采用以下步骤。
1、右键单击“解决方案资源管理器” 中的“ApiClient” 节点以打开“属性页” 对话框。
2、在“配置”下拉框中,选择“所有配置”(如果尚未选择) 。
3、在左窗格中,选择“配置属性” > “C/C++” > “常规” 。
4、在属性窗格中,选择“附加包含目录” 编辑框旁的下拉控件,然后选择“编辑”
5、在“附加包含目录” 对话框的顶部窗格中双击以启用编辑控件。 或者,选择文件夹图标以创建新条目。
6、在编辑控件中,指定指向 ApiClient.h
头文件的位置的路径。 可选择省略号 (…) 控件浏览到正确的文件夹 。
还可将客户端源文件中的相对路径输入到包含 DLL
头文件的文件夹。 如果已按照指示将客户端项目置于 DLL
的单独解决方案中,则相对路径应如下所示:
..\..\DiyApi\DiyApi
如果 DLL
和客户端项目位于同一解决方案中,则相对路径可能如下所示:
..\DiyApi
如果 DLL
和客户端项目位于其他文件夹中,请调整相对路径以进行匹配。 或者,使用省略号控件浏览文件夹。
7、在“附加包含项目”对话框中输入标头文件的路径后,选择“确定”按钮 。 在“属性页”对话框中,选择“确定”按钮以保存更改 。
现在可以包括 DiyApi.h
文件,并使用它在客户端应用程序中声明的函数。 相应的ApiClient.cpp
的内容如下:
#include "DiyApi.h" #include <iostream> using namespace std; int main() { const magicNumber = 8; /* 获取magicNumber次整数8并输出 */ for(int i=magicNumber;i>0;i--){ cout<<GetNumber_8<<endl; } return 0; }
尝试编译运行编译,很棒