C++ 动态库DLL的使用方法
一、VS生成动态链接库:
-
使用VS直接新建DLL项目会生成一些多余的文件,所以建议新建空项目,写完代码后,在项目属性页->配置属性->常规->配置类型->改为:动态库DLL,最后生成即可
-
生成DLL项目和调用DLL项目配置属性要一致(同样是x64或者x86,隐式调用时必须同为release或debug)
-
导出DLL可以使用
__declspec(dllexport)
(声明一个导出函数,意味着这个函数要从本DLL导出),也可以使用def文件(如果DLL全是C++类的话,无法使用def文件导出指定函数)
二、显式加载动态链接库
显示加载不需要修改项目的任何属性,只需要调用LoadLibrary()
和GetProcAddress()
函数即可
具体步骤:加载dll -> 获取函数地址 -> 使用 -> 卸载
生成DLL文件代码:
//.h文件
#pragma once
#ifdef __cplusplus
extern "C"
{
__declspec(dllexport) int add(int a, int b);
__declspec(dllexport) int sub(int a, int b);
}
#endif // __cplusplus
//.cpp文件
#include "MyDLL.h"
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
加载DLL文件代码
#include <iostream>
#include <Windows.h>
//声明函数指针,指向找到的函数地址,后面通过函数指针调用DLL中定义的函数
typedef int(*FUN_ADD)(int, int);
typedef int(*FUN_SUB)(int, int);
int main()
{
HMODULE hModule = LoadLibrary(L"./DLL/bin/13GenerateDLL");
if (hModule == nullptr)
{
std::cout << "加载DLL失败!\n";
return 0;
}
auto dllFunAdd = (FUN_ADD)GetProcAddress(hModule, "add");
auto dllFunSub = (FUN_ADD)GetProcAddress(hModule, "sub");
if (dllFunAdd == nullptr || dllFunSub == nullptr)
{
std::cout << "加载函数失败!\n";
return 0;
}
std::cout << "3 add 2 :" << dllFunAdd(3, 2) << std::endl;
std::cout << "4 sub 2 :" << dllFunSub(4, 2) << std::endl;
FreeLibrary(hModule);
system("pause");
return 0;
}
说明:
-
使用显示调用方法(LoadLibrary()),需要以C的方式(extern "C")导出DLL,因为C++有类和重载的缘故,导出的函数名会加上一些符号,将函数原名直接传入
GetProcAddress()
返回为0, -
如果要使用C++方式导出,可以使用depend查看函数符号名,将符号传入
GetProcAddress()
即可。例如:auto fun = (FUN_ADD)GetProcAddress(hModule, "?add@@YAHHH@Z");
-
要在导出函数的函数声明之前加上
__declspec(dllexport)
,不然GetProcAddress()
找不到函数 -
对于函数
GetPrcoAddress()
无论项目属性是宽字符还是窄字符,第二个参数都是ANSI -
显式调用必须声明一个函数指针,指向找到的函数地址,通过函数指针调用DLL中定义的函数
-
函数
LoadLibrary()
需要包含头文件Windows.h
,参数为DLL文件路径名,根据自己需要设置。 -
使用完DLL记得卸载
FreeLibrary();
-
可以使用DUMPBIN(VS安装目录下有这个程序)查看.obj文件、.lib库、.dll库、.exe执行文件,包含了哪些函数以及相关的信息(符号清单)DUMPBIN选项
三、隐式加载动态链接库
隐式加载需要在项目属性中设置包含目录以及附加项,可以在程序中直接用函数原名调用DLL中的函数
具体步骤:包含头文件 -> 包含lib文件 -> dll添加正确 -> 调用函数原名
生成DLL文件代码:
//.h文件
#pragma once
__declspec(dllexport) int add(int a, int b);
__declspec(dllexport) int sub(int a, int b);
//.cpp文件
#include "MyDLL.h"
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
加载DLL文件代码:
#include <iostream>
#include <MyDLL.h>
#pragma comment(lib,"13GenerateDLL.lib")
int main()
{
std::cout << "3 add 2 :" << add(3, 2) << std::endl;
std::cout << "4 sub 2 :" << sub(4, 2) << std::endl;
system("pause");
return 0;
}
说明:
-
隐式加载DLL,首先需要将生成DLL的头文件包含到调用DLL的程序中(此处的头文件没有
__declspec(dllexport)
,即只有函数声明),其次再将lib文件包含进去(lib文件会在生成DLL文件时自动生成) -
包含lib有两种方式(以下路径中*为本人项目路径,根据自己实际情况填写):
- 直接在项目属性中的 链接器->输入->附加依赖项 填入lib的路径,此处需要包含lib文件名,例如:***\12LoadDLL\DLL\lib\13GenerateDLL.lib
- 在项目属性中的 链接器->常规->附加库目录 填入lib的路径,此处不需要包含lib文件名,例如:***\12LoadDLL\DLL\lib,然后在调用DLL程序的开头处添加
#pragma comment(lib,"13GenerateDLL.lib")
-
生成的DLL文件必须放到调用DLL程序的exe文件目录下(如果没有正确放置DLL文件,VS2019会报错:无法定位程序输入点...直接双击exe文件(不在vs下调试)会报错:由于找不到*.dll,无法继续执行代码...)
四、VS release断点调试
设置以下选项即可