参考自:http://bbs.csdn.net/topics/330169671
__declspec(dllexport):导出符号,也就是定义需要导出函数的dll中给导出函数的函数声明前面加上导出符号,表示该方法可以导出给其他DLL或者exe使用;
__declspec(dllimport)导入符号,也就是在使用该函数的DLL或者exe中需要在该函数的函数声明前面加上该符号,表示该函数方法是从其他库导入的。
我们编写一个DLL库一般都是用来给其他DLL或者exe程序调用的。当我们编写DLL库时,要想把该库中的函数导出来给其他DLL或者exe使用,
一般有两种方式:一是在声明该函数的声明函数前面加上导出符号__declspec(dllexport);二是在.def文件中写上导出函数。
因为导出函数的头文件一般既要给导出DLL使用,又要给调用该导出DLL函数的DLL或者EXE使用。而导出DLL库给其他DLL或者EXE程序使用的时候,函数是相当于导入该DLL或者EXE的,
所以其头文件的声明中的__declspec(dllexport)导出符号改成__declspec(dllimport)导入符号。
为了方便使用和维护同一分头文件,所以在导出函数的头文件中加上
#ifdef _EXPORTING
#define CLASS_DECLSPEC __declspec(dllexport)
#else
#define CLASS_DECLSPEC __declspec(dllimport)
#endif
这段代码。
我们在该导出DLL的编译选项定义中定义宏 #define _EXPORTING,而在调用该导出函数DLL的其他DLL或者exe的编译选项中不定义宏_EXPORTING。
那么导出头文件和导入头文件就可以只维持一份头文件了,有利于维护。
关于导入头文件中要把__declspec(dllexport)改成 __declspec(dllimport)问题
一、关于 __declspec(dllimport)我刚才找了一下,有人写过相关的文章,大意是说,不用这个也链接器也能工作,不过用它更好。原文是说:
不使用 __declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。
隐式使用dll时,不加__declspec(dllimport)完全可以,使用上没什么区别,只是在生成的二进制代码上稍微有点效率损失。
1、不加__declspec(dllimport)时,在使用dll中的函数时,编译器并不能区别这是个普通函数,还是从其它dll里导入的函数,所以其生成的代码如下:
call 地址1
地址1:
jmp 实际函数地址
2、有__declspec(dllimport)时,编译器知道这是要从外部dll导入的函数,从而在生成的exe的输入表里留有该项,以便在运行exe,PE载入器加载exe时对输入地址表IAT进行填写,这样生成的代码如下:
call dword ptr[输入表里哪项对应的内存地址] (注意:现在就不需要jmp stub了)
二、导入全局、静态或者类成员变量需要__declspec(dllimport)。
#define DllImport __declspec(dllimport)
DllImport int j;
__declspec(dllexport)是用于避免需要自己写DEF文件的。编译器会为被__declspec(dllexport)修饰的函数自动添加一个导出函数入口。如果你在其他模块中包含__declspec(dllexport)的头文件,这些项目的导出表中也会生成一个同名导出函数。