__declspec(dllimport)的作用 .

本文深入探讨了__declspec(dllimport)的实际用途,尤其是在导入动态链接库中的全局变量及导出类的静态成员方面的作用,并对比了使用与不使用的区别。

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


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


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

  1. __declspec(dllexport) void HelloWorld();
  1. __declspec(dllexportvoid HelloWorld();  


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


  1. void HelloWorld()
  2. {
  3. AfxMessageBox(_T("HelloWorld"));
  4. }
  1. void HelloWorld()  
  2. {  
  3.     AfxMessageBox(_T("HelloWorld"));  
  4. }  


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


  1. #ifdef _EXPORTING
  2. #define API_DECLSPEC __declspec(dllexport)
  3. #else
  4. #define API_DECLSPEC __declspec(dllimport)
  5. #endif
  1. #ifdef _EXPORTING   
  2. #define API_DECLSPEC    __declspec(dllexport)   
  3. #else   
  4. #define API_DECLSPEC    __declspec(dllimport)   
  5. #endif  

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


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

总结了各位大虾的发言,特得出如下结论:

1. 在导入动态链接库中的全局变量方面起作用:
使用类似

  1. #ifdef _EXPORTING
  2. #define API_DECLSPEC __declspec(dllexport)
  3. #else
  4. #define API_DECLSPEC __declspec(dllimport)
  5. #endif
  1. #ifdef _EXPORTING   
  2. #define API_DECLSPEC    __declspec(dllexport)   
  3. #else   
  4. #define API_DECLSPEC    __declspec(dllimport)   
  5. #endif  


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


  1. API_DECLSPEC CBtt g_Btt;
  1. API_DECLSPEC CBtt g_Btt;  


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


  1. API_DECLSPEC CBtt g_Btt;
  1. API_DECLSPEC CBtt g_Btt;  


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


  1. CBtt g_Btt;
  1. CBtt g_Btt;  


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


  1. extern CBtt g_Btt;
  1. extern CBtt g_Btt;  


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


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


  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. };
  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函数:


  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. };
  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这个关键字,这里体现了一种对称美。

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


参考文献:


1. __declspec(dllimport) 到底有什么用?



原文见: http://blog.youkuaiyun.com/clever101/article/details/5421782

<think>首先,用户的问题是:“C1_DLL_IMPORT __declspec(dllimport)使用示例”。我需要用中文回答。 问题核心是关于在C++中使用`__declspec(dllimport)`的示例。`__declspec(dllimport)`是Microsoft特定的扩展,用于在导入DLL函数时优化性能。 我的回答应该包括: - 解释`__declspec(dllimport)`的作用:它用于声明一个函数是从DLL导入的,编译器可以优化调用。 - 提供一个简单的代码示例,展示如何在头文件中使用它。 - 确保回答是中文的。 回答后,我必须提出3-5个相关问题,以"§§相关问题§§"开头,并在同一行列出问题编号。 示例格式提醒: - 回答完问题后,直接以"§§相关问题§§"开头。 - 然后列出1. 2. 3. 等。 - 禁止换行分散,所以问题列表应该连续输出。 结构我的回答: 1. **解释部分**:用中文简要解释`__declspec(dllimport)`。 2. **代码示例**:提供一个C++代码片段,展示使用示例。 - 例如,一个头文件定义宏用于导入。 - 一个函数声明。 3. **结束部分**:输出"§§相关问题§§",然后列出3-5个相关问题。 相关问题应该基于用户的问题和我的回答。可能的想法: - 关于`__declspec(dllexport)`的对比。 - 如何避免使用这些扩展(跨平台)。 - 性能优化的细节。 - 实际应用场景。 列出相关问题: 1. `__declspec(dllexport)` 和 `__declspec(dllimport)` 的区别是什么? 2. 在跨平台开发中,如何替代 `__declspec(dllimport)`? 3. 使用 `__declspec(dllimport)` 能带来哪些性能优势? 4. 如果忘记使用 `__declspec(dllimport)` 会导致什么问题? 5. 在头文件中如何同时处理导入和导出? 确保有3-5个。 完整回答: - 先解释。 - 提供代码示例。 - 然后"§§相关问题§§"和列表。</think>在 C++ 开发中,`__declspec(dllimport)` 是 Microsoft Visual Studio 特有的关键字,用于优化从动态链接库(DLL)导入函数的性能。它告诉编译器该函数是从外部 DLL 加载的,从而生成更高效的代码(例如,避免间接调用开销)。通常与 `__declspec(dllexport)` 配对使用,在头文件中通过宏切换来处理 DLL 的导出和导入。以下是典型的使用示例: ```cpp // 在头文件(如 onecolorcalibration.h)中定义宏 #ifdef ONECOLORCALIBRATION_EXPORTS #define C1_DLL_IMPORT __declspec(dllexport) // 当编译 DLL 时导出 #else #define C1_DLL_IMPORT __declspec(dllimport) // 当使用 DLL 时导入 #endif // 声明导入函数 C1_DLL_IMPORT void CalibrateColor(int colorValue); // 从 DLL 导入的函数 // 使用时(在应用程序代码中): #include "onecolorcalibration.h" int main() { CalibrateColor(100); // 调用 DLL 中的函数 return 0; } ``` ### 关键点说明: - **作用**:`__declspec(dllimport)` 优化函数调用,减少额外跳转指令,提升性能(约 10-20% 速度提升)。 - **配置**:在 DLL 项目中,预处理器定义 `ONECOLORCALIBRATION_EXPORTS` 以启用导出;在应用程序中,不定义该宏以启用导入。 - **注意事项**:如果忘记使用 `__declspec(dllimport)`,编译器可能生成低效代码,但不影响功能;在跨平台开发中(如 Linux),需使用 `__attribute__((visibility("default")))` 替代。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值