使用C++开发和使用dll
摘要
在 Visual Studio 中创建 DLL 项目。 在同一个解决方案下创建调用 DLL 的客户端项目,使用静态调用和动态调用方法来调用创建的 DLL。
动态链接库介绍
在 Windows 开发中,DLL(Dynamic Link Library,动态链接库)和 LIB(Library,库文件)是两种常见的代码复用方式。
DLL是一个独立的可执行二进制文件(.dll 扩展名),包含可被多个程序共享的函数、类或资源。它不会被编译到最终的可执行文件(.exe)中,而是在程序运行时被加载到内存中供调用,因此能减少程序体积并实现资源共享。
LIB(库文件)是一个包含静态链接程序所需的所有已编译代码和资源的文件,它在程序编译时被直接编译到可执行文件(.exe)中。成为程序的一部分,使用LIB可以方便其他程序调用它提供的函数和资源。
创建一个DLL项目
为了方便,我会将DLL项目和客户端项目放在一个解决方案(Solution)下,这样可以同时进行开发和测试,同时也可以在客户端项目添加对DLL项目中函数的断点进行调试。首先打开Visual Studio,这里以2022社区版为例。
在菜单栏上,选择“文件”>“新建”>“项目”,打开“创建新项目”对话框 。名字可以随便起

创建后是一个什么也没有的解决方案,下面右击解决方案,选择“添加”>“新建项目...”,打开添加新项目的对话框。搜索dll,选择具有“动态链接库(dll)”,名字可以保持默认。

自带的模板会有framework.h、pch.h等文件。这些文件是在编译的时候进行预编译的,可以加快编译速度,对于一般小规模开发来说,是不需要这些文件的,这里全删除,只留下dllmain.cpp,直接右击移除删除即可,删除后dllmain会报错,需要将头文件的#include "pch.h"修改为#include<windows.h>。然后需要告知VS不需要预编译,右击项目,选择属性,弹出属性对话框,找到C/C++的预编译头,设置为不使用预编译头。

再点击生成,发现可以正常生成。在dll项目目录新建两个文件夹,include和src,在头文件过滤器中添加.h文件,放在include文件夹,在源文件过滤器中添加同名的.cpp文件,放在src文件夹。这里就以MathLibrary为例。
为了方便文件管理,这里创建了include文件夹,在文件夹下面也可以再进行文件夹分类,在MathLibrary.h中写下面代码
1 |
|
这段代码来自参考文档1,是微软官方提供的例子。此头文件声明一些函数以生成通用 Fibonacci 序列,给定了两个初始值。 调用 fibonacci_init(1, 1) 会生成熟悉的 Fibonacci 数字序列。
然后在cpp文件中进行实现 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56// MathLibrary.cpp : Defines the exported functions for the DLL.
// DLL internal state variables:
static unsigned long long previous_; // Previous value, if any
static unsigned long long current_; // Current sequence value
static unsigned index_; // Current seq. position
// Initialize a Fibonacci relation sequence
// such that F(0) = a, F(1) = b.
// This function must be called before any other function.
void fibonacci_init(
const unsigned long long a,
const unsigned long long b)
{
index_ = 0;
current_ = a;
previous_ = b; // see special case when initialized
}
// Produce the next value in the sequence.
// Returns true on success, false on overflow.
bool fibonacci_next()
{
// check to see if we'd overflow result or position
if ((ULLONG_MAX - previous_ < current_) ||
(UINT_MAX == index_))
{
return false;
}
// Special case when index == 0, just return b value
if (index_ > 0)
{
// otherwise, calculate next sequence value
previous_ += current_;
}
std::swap(current_, previous_);
++index_;
return true;
}
// Get the current value in the sequence.
unsigned long long fibonacci_current()
{
return current_;
}
// Get the current index position in the sequence.
unsigned fibonacci_index()
{
return index_;
}
会发现系统提示无法打开"MathLibrary.h",这是因为我们创建了一个include文件,但是还没有加入包含。在解决方案管理器中右击dll项目>
属性 > C++ > 常规 > 附加包含目录
编辑选择include所在路径,最好是使用相对路径,直接输入./include
即可
点击生成,正常生成后会在解决方案文件同级的目录的平台文件下的Debug或Release文件夹中生成dll,这个就是动态链接库文件。
使用一个DLL
动态加载
在同一个解决方案下面创建一个新的控制台项目,用来测试刚刚创建的动态链接库。右击解决方案,选择添加>新建>项目 选择 控制台项目

右击控制台项目,设置为启动项。
在项目的cpp文件中编写如下代码
1 |
|
运行程序,输出如下

这里是直接从dll中获取数据调用,但是大家同为C++代码,可以更加静态一点进行调用。
静态调用
右击控制台项目,选择“配置属性”>“C/C++”>“常规”,将dll项目的include文件夹路径添加到附加包含目录中,这里使用相对路径../Dll1/include
其中Dll1是我的dll项目名称,因为他们在同一个解决方案下面,所以相对路径处理起来还是比较方便的。
在debug路径下会生成lib文件,这个文件是相当于是dll的目录,只要将这个添加到路径中,就可以实现不使用dll句柄HINSTANCE直接对函数进行操作
选择“配置属性”>“链接器”>“输入”。 在属性窗格中,选择“附加依赖项”编辑框旁的下拉控件,然后选择“编辑”。 填入"Dll1.lib",选择“确定”返回到“属性页”对话框。选择“配置属性”>“链接器”>“常规”。 在属性窗格中,选择“附加库目录”编辑框旁的下拉控件,然后选择“编辑”。填入 "../$(IntDir)" 这里使用了VS的宏路径,无论是Debug还是Release都可以生成。都是同一个路径。下面将测试端的main函数修改为
1 |
|
前半部分保持不动,后半部分使用函数调用,因为是在同一个解决方案下面,所以dll不需要手动迁移,可以直接调用。
如果想给dll添加第三方库,可以参考下一篇文章