原文链接:关于 C++ 的 dllexport 和 dllimport
原文链接:C++ - dllexport 与 dllimport 介绍和使用
dllexport
与 dllimport
存储级属性是微软对 C 和 C++ 的扩展,可用于从 DLL 中导出或导入函数、数据、对象(objects)。
dllexport
和 dllimport
均是对 C++ 对 DLL 动态库中的 导出类 进行处理的。在 DLL 中为 导出类 ,在调用 DLL 工程的 exe 中为 导入类。
_declspec(dllexport)
声明一个导出函数,是说这个函数要从本 DLL 导出,我要给别人用。一般用于 DLL 中省掉在 DEF 文件中手工定义导出哪些函数的一个方法。当然,如果你的 DLL 里全是 C++ 的类的话,你无法在 DEF 里指定导出的函数,只能用 __declspec(dllexport)
导出类
__declspec(dllimport)
声明一个导入函数,是说这个函数是从别的 DLL 导入,我要用。
一般用于使用某个 DLL 的 exe 中 不使用 __declspec(dllimport)
也能正确编译代码,但使用 __declspec(dllimport)
使编译器可以生成更好的代码。
编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。
但是,必须使用 __declspec(dllimport)
才能导入 DLL 中使用的变量。
实例
dllexport
暴露的是函数的修饰名(decorated name)1。如果想避免函数名修饰,使用 C 函数或者使用 extern "C"
2修饰 C++ 函数。
示例代码:
DLLClass.h
//DLL项目头文件
#pragma once
#include <string>
#ifdef DLLTEST_EXPORTS
# define DLL_API _declspec(dllexport)
# else
# define DLL_API _declspec(dllimport)
#endif // DLLTEST_EXPORTS
class DLL_API DLLClass
{
public:
int add(int a, int b);
std::string add(std::string, std::string);
static int static_var;
};
extern DLL_API int a;
extern "C" DLL_API int aa;
namespace DLLNamespace
{
extern DLL_API int b;
}
DLL_API int addFunc(int, int);
extern "C" DLL_API int addFunc2(int, int);
DLLClass.cpp
//DLL项目CPP
#include "DLLClass.h"
int DLLClass::add(int a, int b)
{
return a + b;
}
std::string DLLClass::add(std::string s1, std::string s2)
{
return s1 + s2;
}
int DLLClass::static_var = 3;
int a = 1;
int aa = 11;
namespace DLLNamespace
{
int b = 2;
}
int addFunc(int a, int b)
{
return a + b;
}
int addFunc2(int a, int b)
{
return a + b;
}
main.cpp
// 测试代码
#include <iostream>
#include "DLLClass.h"
using namespace std;
int main()
{
DLLClass obj;
cout << obj.add(1, 1) << endl;
cout << obj.add("hello ", "world!") << endl;
cout << "a: " << a << endl;
cout << "b: " << DLLNamespace::b << endl;
cout << "static member: " << DLLClass::static_var << endl;
cout << addFunc(1, 1) << endl;
cout << addFunc2(1, 1) << endl;
}
以下是 dumpbin 查看 DLL 导出的所有定义
ordinal hint RVA name
1 0 0001126C ??4DLLClass@@QAEAAV0@$$QAV0@@Z = @ILT+615(??4DLLClass@@QAEAAV0@$$QAV0@@Z)
2 1 00011050 ??4DLLClass@@QAEAAV0@ABV0@@Z = @ILT+75(??4DLLClass@@QAEAAV0@ABV0@@Z)
3 2 00020004 ?a@@3HA = ?a@@3HA (int a)
4 3 0001151E ?add@DLLClass@@QAE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V23@0@Z = @ILT+1305(?add@DLLClass@@QAE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V23@0@Z)
5 4 000112AD ?add@DLLClass@@QAEHHH@Z = @ILT+680(?add@DLLClass@@QAEHHH@Z)
6 5 0001103C ?addFunc@@YAHHH@Z = @ILT+55(?addFunc@@YAHHH@Z)
7 6 0002000C ?b@DLLNamespace@@3HA = ?b@DLLNamespace@@3HA (int DLLNamespace::b)
8 7 00020000 ?static_var@DLLClass@@2HA = ?static_var@DLLClass@@2HA (public: static int DLLClass::static_var)
9 8 00020008 aa = _aa
10 9 0001148D addFunc2 = @ILT+1160(_addFunc2)
我们可以看到,重载函数实现的原理就是使用函数名修饰(4和5),使用 extern "C"
修饰的函数或变量导出定义时的名字与原函数名或变量名相同(9和10)