__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聲明一個函數:

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


在DllApi.cpp寫這個函數的實現:


[cpp]   view plain copy
  1. void HelloWorld()
  2. {
  3. AfxMessageBox(_T("HelloWorld"));
  4. }
[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
[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
[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;  


然后在調用程序這樣導入:


[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. CBtt g_Btt;  


然后在調用程序這樣導入:


[cpp]   view plain copy
  1. extern 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. };
[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. };
[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。


參考文獻:


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



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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM