文章目录
一、导入导出机制
__declspec(dllexport)
和__declspec(dllimport)
都是在 C/C++ 中用于处理导入和导出函数或变量的修饰符。
1.__declspec(dllexport)
:当你希望将函数或变量导出到 DLL 中,以供其他程序使用时,你需要使用 __declspec(dllexport)
修饰符来标记这些函数或变量。你可以将它们放在函数或变量的声明之前,以告诉编译器在链接阶段将这些函数或变量导出到 DLL。例如:
cpp
// 导出函数到 DLL
__declspec(dllexport) void ExportedFunction();
// 导出变量到 DLL
__declspec(dllexport) int ExportedVariable;
__declspec(dllimport)
:当你在一个程序中需要使用从 DLL(动态链接库) 中导入的函数或变量时,你需要使用__declspec(dllimport)
修饰符来声明这些函数或变量是从 DLL 中导入的。你可以将它们放在函数或变量的声明之前,以告诉编译器在链接阶段从 DLL 中获取相应的定义。例如:
cpp
// 导入 DLL 中的函数声明
__declspec(dllimport) void SomeFunction();
// 导入 DLL 中的变量声明
__declspec(dllimport) extern int SomeVariable;
需要注意的是,__declspec(dllexport)
修饰符会生成 DLL 的导出表,并使得这些函数和变量可被其他程序使用。而 __declspec(dllimport)
修饰符则用于在其他程序中声明从 DLL 中导入的函数和变量,以便正确地链接和使用它们。
一般情况下,你会使用条件编译来定义这些修饰符,以便在不同的情况下选择性地声明为 __declspec(dllexport)
或 __declspec(dllimport)
。例如,在 DLL 的源代码中,你可以使用如下条件编译:
cpp
#ifdef MYLIB_EXPORTS
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif
MYLIB_API void SomeFunction();
MYLIB_API extern int SomeVariable;
请注意,__declspec(dllimport)
和 __declspec(dllexport)
是 Microsoft Visual C++ 编译器的特定扩展,并且在其他编译器中可能有不同的实现方式。在其他平台或编译器中,你可能需要使用其他方法来处理导入和导出符号。
二、 __declspec(dllexport)和 extern "C" __declspec(dllexport)的区别
- 在C++编译器中使用
__declspec(dllexport)
导出的函数或者变量是按照C++的名称修饰规则导出的。C++ 默认使用名称修饰(name mangling)来支持函数重载和特殊类型等功能,这使得导出的函数无法被简单地从 DLL 中调用。 extern "C" __declspec(dllexport)
:这个修饰符通常与 C++ 代码中的函数一起使用,并指示编译器使用 C 语言的命名和链接约定来导出函数。通过使用extern "C"
,你告诉编译器使用 C 语言的命名约定导出函数,并使用__declspec(dllexport)
将其添加到导出表中。
extern "C"
关键字对于纯粹的 C 代码来说通常是多余的,因为 C 本身默认使用 C 语言的命名和链接约定。
在跨语言的调用中一般都是按照C的方式来调用,C导出的函数名或者变量名不会被修饰,在python、C#中调用C/C++的函数或者变量都应该使用C的方式导出使用。
三、示例
要在 C++ 中将全局变量导出到 DLL 中,你需要使用适当的修饰符和导出声明。下面是一个示例,展示如何导出全局变量到 DLL 中:
- 在一个头文件中声明全局变量,并使用
__declspec(dllexport)
修饰符进行导出声明。例如,创建一个名为 "globals.h" 的头文件:
cpp
// globals.h
#ifdef GLOBALS_EXPORTS
#define GLOBALS_API __declspec(dllexport)
#else
#define GLOBALS_API __declspec(dllimport)
#endif
GLOBALS_API extern int globalVar;
在上述代码中,我们使用 GLOBALS_API
宏来指定函数的导入和导出方式。
- 在一个源文件中定义全局变量。例如,创建一个名为 "globals.cpp" 的源文件:
cpp
// globals.cpp
#include "globals.h"
GLOBALS_API int globalVar = 42;
在上述代码中,我们定义了一个全局变量 globalVar
并初始化为 42。
-
将 "globals.h" 和 "globals.cpp" 文件编译成 DLL。这可以通过编译器提供的相关选项或使用构建工具(如 Visual Studio)来完成。确保在生成 DLL 文件时定义
GLOBALS_EXPORTS
宏,以便在头文件中正确地设置__declspec(dllexport)
或__declspec(dllimport)
。 -
在另一个项目中使用该 DLL。包含 "globals.h" 头文件,并使用
__declspec(dllimport)
修饰符进行导入声明。然后,可以像使用任何其他全局变量一样使用导入的全局变量:
cpp
// main.cpp
#include "globals.h"
#include <iostream>
int main() {
std::cout << "Global variable value: " << globalVar << std::endl;
return 0;
}
请注意,导出的全局变量只能在 DLL 内部进行修改,而在主程序中只能进行读取。如果你需要在主程序中修改该全局变量的值并使其对 DLL 产生影响,你需要提供适当的函数接口来实现。(在实际使用时导出函数到DLL中不用把头文件中的__declspec(dllexport)改为__declspec(dllimport), 但是变量必须要,否则会报连接错误,找不到变量)。