C++ 动态库导出
C++ 动态库导出
该文主要介绍 C++
动态库导出。
C++ 动态库导出
1. C 导出函数
1
2
3
4
5
6
7
#ifdef _WIN32
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __attribute__((visibility("default")))
#endif
DLL_API void foo();
2. C++ 导出函数
2.1. 名称修饰
C++
具有名称修饰(Name Mangling) 机制,编译器会修改函数名以包含类名、参数类型等信息。
- 支持函数重载:同名函数可以根据参数类型进行区分。
- 支持不同的命名空间和类:防止不同作用域中的函数/变量产生符号冲突。
- 支持 C++ 特性:如类成员函数、模板、命名空间等,它们都可能引入符号冲突,需要独特的符号名。
2.2. 解决办法
为了避免名称修饰,C++
提供 extern "C"
关键字,使导出的符号保持 C
语言的命名规则
1
2
3
4
5
6
7
8
9
#ifdef _WIN32
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __attribute__((visibility("default")))
#endif
extern "C" {
DLL_API void foo();
}
2.3. MSVC导出问题
这样默认导出可以使导出函数与声明函数名称一致,但是如果再加上调用约定1,就又发生名称修饰。
这个解决办法就是编写 *.def
文件,指定导出函数名称。可以使用 .def
文件来控制 Windows
目标(如 DLL)的导出符号。.def
文件可以显式指定哪些函数要导出,而不需要使用 __declspec(dllexport)
。
2.4. 示例
dll.h
导出函数定义
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
#ifndef __DLL_H__
#define __DLL_H__
#if defined(WIN32) || defined(_WIN32)
#define DLL_CALL __stdcall
#ifdef DLL_EXPORT
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
#elif defined(__linux__)
#define DLL_CALL __attribute__((stdcall))
#define DLL_API __attribute__((visibility("default")))
#else
#define DLL_CALL __attribute__((stdcall))
#define DLL_API
#endif
int Add1(int a, int b);
DLL_API int Add2(int a, int b);
int DLL_CALL Add3(int a, int b);
DLL_API int DLL_CALL Add4(int a, int b);
#ifdef __cplusplus
extern "C" {
#endif
int Add5(int a, int b);
DLL_API int Add6(int a, int b);
int DLL_CALL Add7(int a, int b);
DLL_API int DLL_CALL Add8(int a, int b);
DLL_API int DLL_CALL Add9(int a, int b);
DLL_API int DLL_CALL Add10(int a, int b);
#ifdef __cplusplus
}
#endif
#endif
dll.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
#include "dll.h"
int Add1(int a, int b)
{
return a + b;
}
DLL_API int Add2(int a, int b)
{
return a + b;
}
int DLL_CALL Add3(int a, int b)
{
return a + b;
}
DLL_API int DLL_CALL Add4(int a, int b)
{
return a + b;
}
int Add5(int a, int b)
{
return a + b;
}
DLL_API int Add6(int a, int b)
{
return a + b;
}
int DLL_CALL Add7(int a, int b)
{
return a + b;
}
DLL_API int DLL_CALL Add8(int a, int b)
{
return a + b;
}
DLL_API int DLL_CALL Add9(int a, int b)
{
return a + b;
}
DLL_API int DLL_CALL Add10(int a, int b)
{
return a + b;
}
dll.def
导出函数指定
1
2
3
4
5
6
LIBRARY "dll"
; @可以指定导出的 Ordinal
EXPORTS
Add9
Add10 @1
编译后导出函数如下图所示:
其中 Add1
Add3
Add5
Add7
函数没有导出;Add2
Add4
Add8
有导出,但是函数名称不一致;Add6
Add9
Add10
导出函数与定义函数名称一致;Add10
的 Ordinal
为 1。
- 使用了
DLL_API
是有导出函数 Add6
默认调用约定导出名称一致Add9
Add10
使用def
文件指定导出名称一致
参考
https://learn.microsoft.com/zh-cn/cpp/build/reference/gd-gr-gv-gz-calling-convention?view=msvc-170 ↩︎
本文由作者按照 CC BY 4.0 进行授权