有关动态库总结!(19)

来源:互联网 发布:黑客攻击软件 编辑:程序博客网 时间:2024/06/01 08:06

(19)动态库中的_declspec(import)和_declspec(export):

在导出的时候,添加_declspec(export),告诉编译器此函数需要导出:

_declspec(export) add(int,int);


如果添加extern “C”则告诉编译器,用C语言的不改名方式去导出add(int,int)函数

extern "C" _declspec(export) add(int,int);


对于_declspec(export)这个关键词而言,如果编译器看到它,会在生成的.obj文件中加入一些额外的信息。当链接器在链接Dll所有的.obj时,会解析这些信息。即当链接生成dll文件时,链接器会检测这些额外添加的信息,并且根据这些信息生成一个.lib文件。这个lib文件会列出该dll导出的符号。

除此之外,链接器还会在Dll文件中嵌入一个导出符号表,这个导出段(按字符顺序排列的)列出了导出的变量,函数和类的符号名。并且链接器还会保存相对虚拟地址,表示每个符号可以在DLL模块中的何处找到。(所以在代码中使用LoadLibrary(),GetProcAddress()等函数显式的调用dll)

另外,在使用extern "C" 时,如果使用VC++编译器,并且函数调用方式为_stdcall时,编译器还是会改编其到处的函数名,所以这时只能使用.def文件定义到处符号。具体使用如下:

EXPORTS

add

当编译器解析这个def文件时,会发现add改编后的名字和add都被导出。由于这两个名字是匹配的(不考虑改编),因此,链接器会使用.def中定义的名称导出。当然如果不想创建一个.def文件,可以使用

#pragma comment(linker,"/export:add=_MyFunc@8");


其中的_MyFunc@8是VC++编译器对上诉_stdcall调用方式改编的名字。这个代码的作用是,告诉编译器产生一个连接器指示符,该指示符告诉链接器要导出一个名为add的函数,该函数的入口点与_MyFunc@8相同。需要注意的是:这个仅仅是能让我们避免使用.def文件而已,并没有什么特别之处。


在导入的时候,添加_declspec(import),告诉编译器此函数需要导入:

_declspec(import) add(int,int);

同上,添加extern "C"作用类似。
当然,我们也可以不使用_declspec(import),直接用extern关键词。但是,如果编译器能够提前知道我们要引用的符号是从一个DLL的.lib文件中导入的,那么它将能够产生略微高效的代码。所以推荐大家使用_declspec(import)。

另外需要注意:当编译器看到_declspec(import)时,会知道其因该从某个dll模块导入该符号,但是编译器不知道具体的dll是哪一个,编译器只想确认我们以正确方式来访问这些符号而已。关于具体中从哪个dll导入,实在链接过程中,链接器需要确认代码中引用的符号是来自哪个dll的。因此我们需要将dll的.lib文件传给链接器。
但是.lib仅仅是列出了dll的导出符号而已。所以链接器只是想确认这个符号确实存在,以及这个符号来自哪个dll模块。




原创粉丝点击