【转载】C++ —— dllexport 与 dllimport 介绍和使用

原文链接:关于 C++ 的 dllexport 和 dllimport
原文链接:C++ - dllexport 与 dllimport 介绍和使用

dllexportdllimport 存储级属性是微软对 C 和 C++ 的扩展,可用于从 DLL 中导出导入函数、数据、对象(objects)。

dllexportdllimport 均是对 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)

我们可以看到,重载函数实现的原理就是使用函数名修饰(45),使用 extern "C" 修饰的函数或变量导出定义时的名字与原函数名或变量名相同(910

猜你喜欢

转载自juejin.im/post/7127231628639731726