在Windows DLL編程時,可使用__declspec(dllimport)關鍵字匯入函數或者變數。
函數的匯入 當你需要使用DLL中的函數時,往往不需要顯示地匯入函數,編譯器可自動完成。但如果你顯示地匯入函數,編譯器會產生品質更好的代碼。由於編譯器確切地知道了一個函數是否在一個DLL中,它就可以產生更好的代碼,不再需要間接的調用轉接。 Win32的PE格式(Portable Executable Format)把所有匯入地址放在一個匯入地址表中。下面用一個具體執行個體說明使用__declspec(dllimport)匯入函數和不使用的區別: 假設func是一個DLL中的函數,現在在要產生的.exe的main函數中調用func函數,並且不顯示地匯入func函數(即沒有:__declspec(dllimport)),程式碼範例如下: int main() { func(); } 編譯器將產生類似這樣的調用代碼: call func 然後,連結器把該調用翻譯為類似這樣的代碼: call 0x40000001 ; ox40000001是"func"的地址 並且,連結器將產生一個Thunk,形如: 0x40000001: jmp DWORD PTR __imp_func 這裡的imp_func是func函數在.exe的匯入地址表中的函數槽的地址。然後,載入器只需要在載入時更新.exe的匯入地址表即可。 而如果使用了__declspec(dllimport)顯示地匯入函數,那麼連結器就不會產生Thunk(如果不被要求的話),而直接產生一個間接調用。因此,下面的代碼: __declspec(dllimport) void func1(void);
void main(void)
{
func1();
} 將調用如下調用指令: call DWORD PTR __imp_func1 因此,顯示地匯入函數能有效減少目標代碼(因為不產生Thunk)。另外,在DLL中使用DLL外的函數也可以這樣做,從而提高空間和時間效率。 變數的匯入 與函數不同的是,在使用DLL中的變數時,需要顯示地匯入變數。使用__declspec(dllimport)關鍵字匯入變數。若在DLL中使用.def匯出變數,則應使用DATA修飾變數,而不是使用已經被遺棄的CONSTANT。因為CONSTANT可能需要使用指標間接訪問變數,不確定什麼時候會出問題。
先看代碼:以下是在dev-c++裡建立自已的dll時的dll.h裡面的代碼,這裡面有一個:_declspec(dllexport)
#ifndef _DLL_H_
#define _DLL_H_//防重複定義
#if BUILDING_DLL
# define DLLIMPORT __declspec (dllexport)
#else /* Not BUILDING_DLL */
# define DLLIMPORT __declspec (dllimport)
#endif /* Not BUILDING_DLL */
DLLIMPORT void HelloWorld (void);
#endif /* _DLL_H_ */
上面代碼裡面的_delcspce(dllexport)被定義為宏,這樣可以提高程式的可讀性!這個的作是是將函數定義為匯出函數,也就是說這個函數要被包含這個函數的程式之外的程式調用!本語句中就是:void Helloword(void):
摘自msdn:在 32 位編譯器版本中,可以使用 __declspec(dllexport) 關鍵字從 DLL 匯出資料、函數、類或類成員函數。__declspec(dllexport) 將匯出指令添加到對象檔案
若要匯出函數,__declspec(dllexport) 關鍵字必須出現在呼叫慣例關鍵字的左邊(如果指定了關鍵字)。例如:
__declspec(dllexport) void __cdecl Function1(void);
若要匯出類中的所有公用資料成員和成員函數,關鍵字必須出現在類名的左邊,如下所示:
class __declspec(dllexport) CExampleExport : public CObject{ ... class definition ... };
產生 DLL 時,通常建立一個包含正在匯出的函數原型和/或類的標頭檔,並將 __declspec(dllexport) 添加到標頭檔中的聲明。若要提高代碼的可讀性,請為 __declspec(dllexport) 定義一個宏並對正在匯出的每個符號使用該宏:
#define DllExport __declspec( dllexport )
__declspec(dllexport) 將函數名儲存在 DLL 的匯出表中。
若要確定用於匯出函數的方法(.def 檔案或 __declspec(dllexport) 關鍵字),請回答下列問題:
使用 .DEF 檔案的優缺點
在 .def 檔案中匯出函數使您得以控制匯出序號。當將附加的匯出函數添加到 DLL 時,可以給它們分配更高的序號值(高於任何其他匯出函數)。當您進行此操作時,使用隱式連結的應用程式不必與包含新函數的新匯入庫重新連結。這非常重要,例如,在設計將由許多應用程式使用的第三方 DLL 時。可以通過添加附加功能不斷地增強 DLL,同時確保現有應用程式繼續正常使用新的 DLL。MFC DLL 是使用 .def 檔案產生的。
使用 .def 檔案的另一個優點是:可以使用 NONAME 屬性匯出函數,該屬性僅將序號放到 DLL 的匯出表中。對具有大量匯出函數的 DLL,使用 NONAME 屬性可以減小 DLL 檔案的大小。有關編寫模組定義語句的資訊,請參見模組定義語句的規則。有關序號匯出的更多資訊,請參見按序號而不是按名稱從 DLL 匯出函數。
使用 .def 檔案的主要缺點是:在 C++ 檔案中匯出函數時,必須將修飾名放到 .def 檔案中,或者通過使用外部“C”用標準 C 連結定義匯出函數,以避免編譯器進行名稱修飾。
如果需要將修飾名放到 .def 檔案中,則可以通過使用 DUMPBIN 工具或 /MAP 連結器選項來擷取修飾名。請注意,編譯器產生的修飾名是編譯器特定的。如果將 Visual C++ 編譯器產生的修飾名放到 .def 檔案中,則連結到 DLL 的應用程式必須也是用相同版本的 Visual C++ 產生的,這樣調用應用程式中的修飾名才能與 DLL 的 .def 檔案中的匯出名相匹配。
使用 __declspec(dllexport) 的優缺點
使用 __declspec(dllexport) 非常方便,因為不必考慮維護 .def 檔案和擷取匯出函數的修飾名。例如,如果您設計的 DLL 供自己控制的應用程式使用,則此方法很適用。如果通過新的匯出函數重建 DLL,還必須重建應用程式,因為如果使用不同版本的編譯器進行重新編譯,則匯出的 C++ 函數的修飾名可能會發生變化。