AI: windows-dllexport-dllimport

在 C++ 中,__declspec(dllexport)__declspec(dllimport) 是用于标记 动态链接库(DLL) 中符号导出和导入的关键字。它们是 Microsoft 编译器(如 Visual Studio)专有的修饰符,用于控制符号在动态链接库中的可见性。

以下是详细介绍:


1. __declspec(dllexport)

__declspec(dllexport) 告诉编译器将函数或变量导出(Export)到动态链接库(DLL),以便其他模块(如可执行文件或其他 DLL)可以使用这些符号。

使用场景

  • 当您创建一个动态链接库(DLL)时,需要使用 __declspec(dllexport) 导出希望公开的函数、变量或类。

示例

导出函数

1
2
// MyLibrary.h
__declspec(dllexport) void myFunction();
1
2
3
4
5
6
// MyLibrary.cpp
#include "MyLibrary.h"

__declspec(dllexport) void myFunction() {
// 函数实现
}

导出变量

1
2
// MyLibrary.h
__declspec(dllexport) int myGlobalVar;
1
2
3
4
// MyLibrary.cpp
#include "MyLibrary.h"

__declspec(dllexport) int myGlobalVar = 42;

导出类

1
2
3
4
5
// MyLibrary.h
class __declspec(dllexport) MyClass {
public:
void myMethod();
};
1
2
3
4
5
6
// MyLibrary.cpp
#include "MyLibrary.h"

void MyClass::myMethod() {
// 函数实现
}

2. __declspec(dllimport)

__declspec(dllimport) 告诉编译器从动态链接库(DLL)中导入(Import)符号,以便在当前模块中使用。

使用场景

  • 当您使用一个动态链接库(DLL)中的函数、变量或类时,需要使用 __declspec(dllimport) 来声明这些符号。

示例

假设您已经创建了一个动态链接库,并导出了一个函数或变量:

导入函数

1
2
// MyLibrary.h
__declspec(dllimport) void myFunction();
1
2
3
4
5
6
7
// main.cpp
#include "MyLibrary.h"

int main() {
myFunction(); // 调用来自 DLL 的函数
return 0;
}

导入变量

1
2
// MyLibrary.h
__declspec(dllimport) int myGlobalVar;
1
2
3
4
5
6
7
// main.cpp
#include "MyLibrary.h"

int main() {
int value = myGlobalVar; // 使用来自 DLL 的全局变量
return 0;
}

导入类

1
2
3
4
5
// MyLibrary.h
class __declspec(dllimport) MyClass {
public:
void myMethod();
};
1
2
3
4
5
6
7
8
// main.cpp
#include "MyLibrary.h"

int main() {
MyClass obj;
obj.myMethod(); // 调用来自 DLL 的方法
return 0;
}

3. 动态切换 __declspec(dllexport)__declspec(dllimport)

在同一个头文件中,通常需要根据编译目标(DLL 还是客户端)动态切换 __declspec(dllexport)__declspec(dllimport)

实现方法

通过定义预处理器宏来控制导出和导入。例如:

1
2
3
4
5
6
7
8
// MyLibrary.h
#ifdef MYLIBRARY_EXPORTS
#define MYLIBRARY_API __declspec(dllexport) // 编译 DLL 时使用
#else
#define MYLIBRARY_API __declspec(dllimport) // 使用 DLL 时使用
#endif

MYLIBRARY_API void myFunction();

在编译 DLL 时定义 MYLIBRARY_EXPORTS,而在使用 DLL 时不定义 MYLIBRARY_EXPORTS

编译动态库(DLL)

在生成 DLL 时,通过编译器选项定义 MYLIBRARY_EXPORTS

1
cl /D MYLIBRARY_EXPORTS MyLibrary.cpp /LD

使用动态库

在使用 DLL 的项目中,不需要定义 MYLIBRARY_EXPORTS,编译器会自动将其视为 __declspec(dllimport)


4. 注意事项

4.1 不使用 __declspec 的情况

  • 如果不使用 __declspec(dllexport) 导出符号,编译器不会将符号公开到 DLL 导出表中,外部模块无法直接访问它们。
  • 如果不使用 __declspec(dllimport) 导入符号,编译器会假定符号是静态链接的,但可能会导致运行时错误。

4.2 使用 DEF 文件导出符号

除了使用 __declspec(dllexport),还可以通过模块定义文件(.def 文件)导出符号。这在需要大量导出符号时更加方便:

示例 DEF 文件

1
2
3
4
LIBRARY MyLibrary
EXPORTS
myFunction
myGlobalVar

生成 DLL

1
cl MyLibrary.cpp /LD /DEF:MyLibrary.def

4.3 导出 C 函数(避免 C++ 名字修饰)

如果导出的函数是用 C++ 编写的,默认情况下会启用名字修饰(name mangling)。为了避免名字修饰,可以使用 extern "C"

1
extern "C" __declspec(dllexport) void myFunction();

5. 总结

  • **__declspec(dllexport)**:用于将符号导出到 DLL,供其他模块使用。
  • **__declspec(dllimport)**:用于从 DLL 导入符号到当前模块。
  • 动态切换导出/导入:使用预处理器宏(如 MYLIBRARY_API)动态控制。
  • DEF 文件:在需要导出大量符号时可以替代 __declspec(dllexport)

如有具体问题或代码场景需要分析,可以进一步探讨! 😊

------ 本文结束 ------
------ 版权声明:转载请注明出处 ------