DLL 是一个包含可由多个程序同时使用的代码和数据的库。 例如,在 Windows 操作系统中,Comdlg32 DLL 执行与对话框有关的常见函数。 因此,每个程序都可以使用该 DLL 中包含的功能来实现“打开”对话框。 这有助于促进代码重用和内存的有效使用。

通过使用 DLL,程序可以实现模块化,由相对独立的组件组成。 例如,一个计帐程序可以按模块来销售。 可以在运行时将各个模块加载到主程序中(如果安装了相应模块)。 因为模块是彼此独立的,所以程序的加载速度更快,而且模块只在相应的功能被请求时才加载。

http://www.jyguagua.com/?p=3341

c++调用.lib的方法

https://developer.aliyun.com/article/247052

隐式加载

显式加载

函数的导出方式

使用exten “C” C语言方式导出

extern "C"的作用是声明以c语言的格式编译当前代码:

  • c语言没有函数重载

  • 编译后的函数名若有参数以”xxx@数字”结尾,“数字”为所有参数占用的内存大小(4位对齐);若无参数则结尾不含”@数字”

  • 编译后的开头字符与调用约定__cdecl(无开头字符)、__**stdcall(以‘_’开头)__fastcall(以‘@’开头)**有关

网络上大部分资料都是使用这种方式。

使用C++方式导出

默认情况下,不加extern "C",将使用C++方式导出;

Demo示例

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
//calc.cpp
#include "pch.h"
#include <iostream>
#include "calc.h"

using namespace std;
using namespace CalcDemo;

Calc::Calc()
{

}

Calc::~Calc()
{

}


int Calc::add(int a, int b)
{
return (a + b);
}


int sub(int a, int b)
{
return (a - b);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//calc.h
#pragma once

#ifdef DLLEXPORTDEMO_EXPORTS
#define DLLEXPORTDEMO_API __declspec(dllexport)
#else
#define DLLEXPORTDEMO_API
#endif


extern "C" DLLEXPORTDEMO_API int sub(int a, int b);


namespace CalcDemo {
class DLLEXPORTDEMO_API Calc {
Calc();
virtual ~Calc();
int add(int a, int b);
};
};

查看dll–使用Dependencies

C++ 显示调用dll

  • demo:
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
#include <iostream>
#include <Windows.h>

using namespace std;

typedef int (*run_sub_t)(int a, int b);
typedef int (*run_add_t)(int a, int b);



int main()
{
std::cout << "Hello World!\n";

HINSTANCE hdll;
run_sub_t runsub = NULL;
run_add_t runadd = NULL;


hdll = LoadLibrary(TEXT("dll_export_demo.dll"));
if (NULL == hdll) {
cout << "LoadLibrary fail." << endl;
goto exit;
}

runsub = (run_sub_t)GetProcAddress(hdll, "sub");
if (NULL == runsub) {
cout << "get run sub process address fail." << endl;
goto exit;
}
cout << "sub(1, 2) = 1-3 = " << runsub(1, 3) << endl;

runadd = (run_add_t)GetProcAddress(hdll, "add");
if (NULL == runadd) {
cout << "get run add process address fail." << endl;
goto exit;
}

cout << "add(5, 6) = " << runadd(5, 6) << endl;

if (NULL != hdll) {
FreeLibrary(hdll);
}


return 0;

exit:
if (NULL != hdll) {
FreeLibrary(hdll);
}

return -1;
}

  • 编译运行报错

因为c++函数名导出方式,add()函数的函数名不是叫add,所有无法找到这个函数。

使用Dependencies打开dll文件,查看add()函数的c++导出函数名,修改重新编译运行:

修改之后,函数add()可以找到了,但是运行抛异常了;分析之后发现,是因为C++所有的成员函数中都有一个隐含的this指针,所以此处add应该有三个参数,直接传递两个参数,是非法传参。

解决方法:将add()函数定义为static函数:

编译运行,再次报错:找不到add()函数:

因为修改了函数为static,所有c++导出的add()函数名发生了变化,修改后重新编译正常:

这样的函数名使用起来总是有些不方便,要使用和代码相同的常规的函数名,一般有两种方法:

  • 声明为exten "C",表示使用C语言方式编译,但是对于一些函数需要c++特性,比如C++的变量类型,C++的重载等,这样就没法实现;
  • 链接时使用def文件,指定函数的名字;

使用def文件生成常规名字的dll

1
2
3
LIBRARY  dll_export_demo
EXPORTS
add

vs链接器配置:

重新编译,查看dll:

直接使用add函数名重新编译运行,工作正常:

参考引用

https://www.cnblogs.com/houkai/archive/2013/06/05/3119513.html

https://blog.csdn.net/lwwl12/article/details/86655488

https://www.codeproject.com/Articles/28969/HowTo-Export-C-classes-from-a-DLL

https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/fatal-error-c1010?view=vs-2019