看到X264中的x264.h中用到了__declspec(dllimport),决定一探究竟。
1. __declspec(dllimport)的作用
我相信写WIN32程序的人,做过DLL,都会很清楚__declspec(dllexport)的作用,它就是为了省掉在DEF文件中手工定义导出哪些函数的一个方法。当然,如果你的DLL里全是C++的类的话,你无法在DEF里指定导出的函数,只能用__declspec(dllexport)导出类。
但是对__declspec(dllimport) 则了解不多。
MSDN上的文档:不使用 __declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。
可以看到,x264里用到了__declspec(dllimport)声明的也都是一些全局变量,可见,“必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量”确实是这样的(除了全局变量,在C++中的类静态成员变量也需要__declspec(dllimport)声明)。
全局变量:
不用__declspec(dllimport)的话,存在一些问题
#include <stdio.h> #pragma comment(lib,"dllTest.lib") extern int dllGlobalVar; int main(int argc, char *argv[]) { printf("%d ", *(int*)dllGlobalVar); *(int*)dllGlobalVar = 1; printf("%d ", *(int*)dllGlobalVar); return 0; }
特别要留意的是用extern int dllGlobalVar声明所导进的并不是DLL中全局变量本身,而是其地址,应用程序必须通过强制指针转换来使用DLL中的全局变量。这一点,从*(int*)dllGlobalVar可以看出。因此在采用这种方式引用DLL全局变量时,千万不要进行这样的赋值操纵:
dllGlobalVar = 1;
其结果是dllGlobalVar指针的内容发生变化,程序中以后再也引用不到DLL中的全局变量了。
在应用工程中引用DLL中全局变量的一个更好方法是:
通过_declspec(dllimport)方式导进的就是DLL中全局变量本身而不再是其地址了,笔者建议在一切可能的情况下都使用这种方式。
#include <stdio.h> #pragma comment(lib,"dllTest.lib") extern int _declspec(dllimport) dllGlobalVar; //用_declspec(dllimport)导进 int main(int argc, char *argv[]) { printf("%d ", dllGlobalVar); dllGlobalVar = 1; //这里就可以直接使用, 无须进行强制指针转换 printf("%d ", dllGlobalVar); return 0; }
2. __declspec(dllimport)使用方法(最佳实践)
另外,在写dll的时候,一般使用这样的头文件写法:
#ifdef _EXPORTING #define API_DECLSPEC __declspec(dllexport) #else #define API_DECLSPEC __declspec(dllimport) #endif
考虑一个情况:若DLL1.CPP是源,DLL2.CPP使用了DLL1中的函数,但同时DLL2也是一个DLL,也要输出一些函数供Client.CPP使用。那么在DLL2中如何声明所有的函数,其中包含了从DLL1中引入的函数,还包括自己要输出的函数。这个时候就需要同时使用__declspec(dllexport)和__declspec(dllimport)了。前者用来修饰本dll中的输出函数,后者用来修饰从其它dll中引入的函数。
在DLL1工程中定义DLL_DLL1_EXPORTS宏,DLL2工程中定义DLL_DLL2_EXPORTS宏即可。
DLL1.h
#ifdef DLL_DLL1_EXPORTS #define DLL_DLL1_API __declspec(dllexport) #else #define DLL_DLL1_API __declspec(dllimport) #endif DLL_DLL1_API void FuncInDll1(void); DLL_DLL1_API void FuncInDll1(int);
DLL2.h
#include"dll1.h" #ifdef DLL_DLL2_EXPORTS #define DLL_DLL2_API __declspec(dllexport) #else #define DLL_DLL2_API __declspec(dllimport) #endif DLL_DLL2_API void FuncInDll2(void); DLL_DLL2_API void FuncInDll2(int);
在DLL2工程中,由于没有定义DLL_DLL1_EXPORTS宏,那DLL1中的函数正好用__declspec(dllimport)声明,符合要求。
参考资料:http://topic.csdn.net/u/20100322/00/17389242-a3f7-46d1-992b-ae4c4e2976bb.html
http://www.blogjava.net/wxb_nudt/archive/2007/09/11/144371.html