__declspec(dllimport)的用途

__declspec(dllexport)

声明一个导出函数/变量,这个函数/变量可以从本DLL导出。意思是在dll外部可以使用这个函数,这种声明形式一般出现在dll的头文件中。
此声明可以省掉在DEF文件中手工定义导出哪些函数的一个方法。当然,如果你的DLL里全是C++的类的话,你无法在DEF里指定导出的函数,只能用__declspec(dllexport)导出类。

__declspec(dllimport)

声明一个导入函数/变量,表明这个函数/变量是从别的DLL导入的,我要在本文件中使用。一般用于需要使用外部dll中某些函数/变量的exe工程头文件中。
不使用 __declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。

但是,如果使用2个头文件,这2个头文件的大部分内容是重复的,只是一个是__declspec(dllexport),另一个是__declspec(dllimport)。而且,如果改动一个头文件,另一个也要做相应的改动。这样就带来了极大的不便,那么,如果使用了宏,就可以将2个头文件合二为一,使用同一个头文件即可,在不同的环境下使用不同的代码段。

因为这个头文件既要被定义该函数的dll包含,也要被使用该函数的程序包含,当被前者包含时我们希望使用__declspec(dllexport)定义函数,当被后者包含时我们希望使用dllimport。于是我们使用
#ifdef _EXPORTING
#define CLASS_DECLSPEC __declspec(dllexport)
#else
#define CLASS_DECLSPEC __declspec(dllimport)
#endif
这种技巧,在定义该函数的dll中,其编译选项定义了_EXPORTING而使用该函数的程序则没有定义。


可能有人会问:__declspec(dllimport)和__declspec(dllexport)是一对的,在动态链接库中__declspec(dllexport)用于导出,__declspec(dllimport)用于导入,就像一个国家一样,有出口也有进口,有什么难理解的呢?这是一种很自然的思路,开始我也是这样理解。


     但是在两年前的一个项目中,我发现不用__declspec(dllimport)似乎也可以。比如现在我新建一个使用共享MFC DLL的规则DLL工程:DllDlg。然后我新建两个文件:DllApi.h和DllApi.cpp。DllApi.h作为接口文 件,DllApi.cpp作为实现文件。


     接着在DllApi.h声明一个函数:

[cpp]  view plain copy
  1. __declspec(dllexportvoid HelloWorld();  


在DllApi.cpp写这个函数的实现:


[cpp]  view plain copy
  1. void HelloWorld()  
  2. {  
  3.     AfxMessageBox(_T("HelloWorld"));  
  4. }  


        这样外部的应用程序或dll就能调用HelloWorld函数。这里要特别提醒的是:有些网友说要把DllApi.h中的__declspec(dllexport) void HelloWorld();改为__declspec(dllimport) void HelloWorld();才能提供给外部调用,实际上这并不需要,这个我已经测试过。从那时我就产生一个疑问:照这样,像类似下面的:


[cpp]  view plain copy
  1. #ifdef _EXPORTING  
  2. #define API_DECLSPEC    __declspec(dllexport)  
  3. #else  
  4. #define API_DECLSPEC    __declspec(dllimport)  
  5. #endif  

      是不是就只剩下一种作用:让外部调用者看得更自然些,知道哪些接口是自己工程需要导入的?__declspec(dllimport)是不是一点实际作用都没有呢?这个疑问一直盘旋在我的脑海。直到最近,我在CSDN论坛上发了一个帖子:


__declspec(dllimport) 的作用到底在哪里呢? 

总结了各位大虾的发言,特得出如下结论:
 
1. 在导入动态链接库中的全局变量方面起作用:
使用类似

[cpp]  view plain copy
  1. #ifdef _EXPORTING  
  2. #define API_DECLSPEC    __declspec(dllexport)  
  3. #else  
  4. #define API_DECLSPEC    __declspec(dllimport)  
  5. #endif  


可以更好地导出dll中的全局变量,比如下面的宏,可以在dll中这样导出全局变量:


[cpp]  view plain copy
  1. API_DECLSPEC CBtt g_Btt;  


然后在调用程序这样导入:


[cpp]  view plain copy
  1. API_DECLSPEC CBtt g_Btt;  


当然也可以使用extern关键字,比如在dll中这样导出全局变量:


[cpp]  view plain copy
  1. CBtt g_Btt;  


然后在调用程序这样导入:


[cpp]  view plain copy
  1. extern CBtt g_Btt;  


但据说使用__declspec(dllimport)更有效。


2. __declspec(dllimport)的作用主要体现在导出类的静态成员方面,
比如在动态链接库中定义这样一个导出类:


[cpp]  view plain copy
  1. class __declspec(dllexport) CBtt  
  2. {  
  3. public:  
  4.     CBtt(void);  
  5.     ~CBtt(void);  
  6. public:  
  7.     CString m_str;  
  8.     static int GetValue()  
  9.     {  
  10.         return m_nValue;  
  11.     }  
  12. private:  
  13.     static int m_nValue;  
  14. };  


照上面这样声明,外部虽然可以使用CBtt类,但不能使用CBtt类的GetValue函数,一使用就会出现无法解析的外部符号 "public: static int CBtt::m_nValue" (?m_nValue@CBtt@@2HA)。只有如下声明才能使用CBtt类的GetValue函数:


[cpp]  view plain copy
  1. #ifdef _EXPORTING  
  2. #define API_DECLSPEC    __declspec(dllexport)  
  3. #else  
  4. #define API_DECLSPEC    __declspec(dllimport)  
  5. #endif  
  6. class API_DECLSPEC CBtt  
  7. {  
  8. public:  
  9.     CBtt(void);  
  10.     ~CBtt(void);  
  11. public:  
  12.     CString m_str;  
  13.     static int GetValue()  
  14.     {  
  15.         return m_nValue;  
  16.     }  
  17. private:  
  18.     static int m_nValue;  
  19. };  



3. 使用隐式使用dll时,不加__declspec(dllimport)完全可以,使用上没什么区别,只是在生成的二进制代码上稍微有点效率损失。


a、 不加__declspec(dllimport)时,在使用dll中的函数时,编译器并不能区别这是个普通函数,还是从其它dll里导入的函数,所以其生 成的代码如下:

call 地址1

地址1:
jmp 实际函数地址


b、有 __declspec(dllimport)时,编译器知道这是要从外部dll导入的函数,从而在生成的exe的输入表里留有该项,以便在运行 exe,PE载入器加载exe时对输入地址表IAT进行填写,这样生成的代码如下:

call dword ptr[输入表里哪项对应的内存地址] (注意:现在就不需要jmp stub了)。这里
有兴趣的朋友可以参看《编译原理》和 PE文件格式。


4.使用__declspec(dllimport)体现了语言的一种对称美,比如虽然!true就是表示false,但是我们还是需要false这个关键字,这里体现了一种对称美。

在此特别感谢CSDN的众位大侠:superdiablo、wltg2001、ccpaishi、jszj、WizardK、hurryboylqs、jingzhongrong、jameshooo、glacier3d、winnuke、starnight1981、laiyiling、yang79tao、ForestDB、zhouzhipen、lxlsymbome、Beyond_cn。

转:http://blog.csdn.net/clever101/article/details/5421782

http://blog.csdn.net/Repeaterbin/article/details/4269666

http://www.cnblogs.com/xd502djj/archive/2010/09/21/1832493.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值