__declspec(dllimport)的作用
轉自:http://blog.csdn.net/mniwc/article/details/7993361
是時候總結一下__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宣告一個函式:
- __declspec(dllexport) void HelloWorld();
- __declspec(dllexport) void HelloWorld();
在DllApi.cpp寫這個函式的實現:
- void HelloWorld()
- {
- AfxMessageBox(_T("HelloWorld"));
- }
- void HelloWorld()
- {
- AfxMessageBox(_T("HelloWorld"));
- }
這樣外部的應用程式或dll就能呼叫HelloWorld函式。這裡要特別提醒的是:有些網友說要把DllApi.h中的__declspec(dllexport) void HelloWorld();改為__declspec(dllimport) void HelloWorld();才能提供給外部呼叫,實際上這並不需要,這個我已經測試過。從那時我就產生一個疑問:照這樣,像類似下面的:
- #ifdef _EXPORTING
- #define API_DECLSPEC __declspec(dllexport)
- #else
- #define API_DECLSPEC __declspec(dllimport)
- #endif
- #ifdef _EXPORTING
- #define API_DECLSPEC __declspec(dllexport)
- #else
- #define API_DECLSPEC __declspec(dllimport)
- #endif
是不是就只剩下一種作用:讓外部呼叫者看得更自然些,知道哪些介面是自己工程需要匯入的?__declspec(dllimport)是不是一點實際作用都沒有呢?這個疑問一直盤旋在我的腦海。直到最近,我在CSDN論壇上發了一個帖子:
__declspec(dllimport) 的作用到底在哪裡呢?
總結了各位大蝦的發言,特得出如下結論:
1. 在匯入動態連結庫中的全域性變數方面起作用:
使用類似
- #ifdef _EXPORTING
- #define API_DECLSPEC __declspec(dllexport)
- #else
- #define API_DECLSPEC __declspec(dllimport)
- #endif
- #ifdef _EXPORTING
- #define API_DECLSPEC __declspec(dllexport)
- #else
- #define API_DECLSPEC __declspec(dllimport)
- #endif
可以更好地匯出dll中的全域性變數,比如按照的巨集,可以在dll中這樣匯出全域性變數:
- API_DECLSPEC CBtt g_Btt;
- API_DECLSPEC CBtt g_Btt;
然後在呼叫程式這樣匯入:
- API_DECLSPEC CBtt g_Btt;
- API_DECLSPEC CBtt g_Btt;
當然也可以使用extern關鍵字,比如在dll中這樣匯出全域性變數:
- CBtt g_Btt;
- CBtt g_Btt;
然後在呼叫程式這樣匯入:
- extern CBtt g_Btt;
- extern CBtt g_Btt;
但據說使用__declspec(dllimport)更有效。
2. __declspec(dllimport)的作用主要體現在匯出類的靜態成員方面,
比如在動態連結庫中定義這樣一個匯出類:
- class __declspec(dllexport) CBtt
- {
- public:
- CBtt(void);
- ~CBtt(void);
- public:
- CString m_str;
- static int GetValue()
- {
- return m_nValue;
- }
- private:
- static int m_nValue;
- };
- class __declspec(dllexport) CBtt
- {
- public:
- CBtt(void);
- ~CBtt(void);
- public:
- CString m_str;
- static int GetValue()
- {
- return m_nValue;
- }
- private:
- static int m_nValue;
- };
照上面這樣宣告,外部雖然可以使用CBtt類,但不能使用CBtt類的GetValue函式,一使用就會出現無法解析的外部符號 "public: static int CBtt::m_nValue" (?m_nValue@CBtt@@2HA)。只有如下宣告才能使用CBtt類的GetValue函式:
- #ifdef _EXPORTING
- #define API_DECLSPEC __declspec(dllexport)
- #else
- #define API_DECLSPEC __declspec(dllimport)
- #endif
- class API_DECLSPEC CBtt
- {
- public:
- CBtt(void);
- ~CBtt(void);
- public:
- CString m_str;
- static int GetValue()
- {
- return m_nValue;
- }
- private:
- static int m_nValue;
- };
- #ifdef _EXPORTING
- #define API_DECLSPEC __declspec(dllexport)
- #else
- #define API_DECLSPEC __declspec(dllimport)
- #endif
- class API_DECLSPEC CBtt
- {
- public:
- CBtt(void);
- ~CBtt(void);
- public:
- CString m_str;
- static int GetValue()
- {
- return m_nValue;
- }
- private:
- static int m_nValue;
- };
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
相關文章
- __declspec(dllexport)和__declspec(dllimport)ExportImport
- 理解 __declspec(dllexport)和__declspec(dllimport)ExportImport
- extern "C" __declspec(dllexport)Export
- _declspec(naked) 使用(裸函式)函式
- [引]C# DllImport 屬性C#Import
- C#中DllImport用法彙總C#Import
- [P/Invoke] 使用 `SetDllImportResolver`[^1] 改寫 `DllImport` 的庫解析規則Import
- dll的def檔案與__declspec(dllexport)匯出函式方式比較Export函式
- DllImport進階:引數配置與高階主題探究Import
- js的作用域、作用域鏈JS
- js的作用域和作用域鏈JS
- js的作用域與作用域鏈JS
- Java中的volatile的作用和synchronized作用Javasynchronized
- Symbol 的作用Symbol
- jQuery $ 的作用jQuery
- #include的作用
- Spring的作用域以及RequestContextListener作用SpringContext
- http 代理的作用HTTP
- JS的作用域JS
- Hashcode的作用
- Git分支的作用Git
- condition的作用
- 作用域的理解
- source命令的作用
- spring的作用Spring
- Oracle Undo的作用Oracle
- SMON 程式的作用
- cookie的作用域Cookie
- SSH Agent 的作用
- noEmit 的作用是?MIT
- Kafka——zookeeper的作用Kafka
- ContentPresenter 的作用
- oracle的undo的作用Oracle
- Vue中key的作用Vue
- Symbol 的作用[翻譯]Symbol
- JAVA中initCause()的作用Java
- mock.js 的作用MockJS
- Token 的作用和原理