動態鏈接庫中定義有兩種函數:導出函數(export function)和內部函數(internal function)。導出函數可以被其它模塊調用,內部函數在定義它們的DLL程序內部使用。
輸出函數的方法有以下幾種:
1、傳統的方法
在模塊定義文件的 EXPORT 部分指定要輸入的函數或者變量。語法格式如下:
entryname[=internalname] [@ordinal[NONAME]] [DATA] [PRIVATE]
其中:
entryname 是輸出的函數或者數據被引用的名稱; internalname 同 entryname; @ordinal 表示在輸出表中的順序號(index); NONAME 僅僅在按順序號輸出時被使用(不使用 entryname ); DATA 表示輸出的是數據項,使用 DLL 輸出數據的程序必須聲明該數據項為 _declspec(DLLimport)。
上述各項中,只有 entryname 項是必須的,其他可以省略。
對於"C"函數來說,entryname 可以等同於函數名;但是對 "C++" 函數(成員函數、非成員函數)來說,entryname 是修飾名。可以從 .map 映像文件中得到要輸出函數的修飾名,或者使用DUMPBIN /SYMBOLS 得到,然後把它們寫在 .def 文件的輸出模塊。DUMPBIN 是VC提供的一個工具。
如果要輸出一個 "C++" 類,則把要輸出的數據和成員的修飾名都寫入 .def 模塊定義文件。
2、在命令行輸出
對鏈接程序 LINK 指定 /EXPORT 命令行參數,輸出有關函數。
3、使用 MFC 提供的修飾符號 _declspec(DLLexport)
在要輸出的函數、類、數據的聲明前加上 _declspec(DLLexport) 修飾符表示輸出。__declspec(DLLexport) 在 C 調用約定、C 編譯情況下可以去掉輸出函數名的下劃線前綴。extern "C" 使得在 C++ 中使用 C 編譯方式成為可能。在"C++"下定義"C"函數需要加 extern "C" 關鍵詞。用 extern "C" 來指明該函數使用 C 編譯方式。輸出的 "C" 函數可以從 "C" 代碼裡調用。
例如,在一個 C++ 文件中,有如下函數:
extern "C" {void __declspec(DLLexport) __cdecl Test(int var);}
其輸出函數名為:Test
MFC提供了一些宏,就有這樣的作用。
AFX_CLASS_IMPORT:__declspec(DLLexport) AFX_API_IMPORT:__declspec(DLLexport) AFX_DATA_IMPORT:__declspec(DLLexport) AFX_CLASS_EXPORT:__declspec(DLLexport) AFX_API_EXPORT:__declspec(DLLexport) AFX_DATA_EXPORT:__declspec(DLLexport) AFX_EXT_CLASS: #ifdef _AFXEXT AFX_CLASS_EXPORT #else AFX_CLASS_IMPORT AFX_EXT_API:#ifdef _AFXEXT AFX_API_EXPORT #else AFX_API_IMPORT AFX_EXT_DATA:#ifdef _AFXEXT AFX_DATA_EXPORT #else AFX_DATA_IMPORT
像 AFX_EXT_CLASS 這樣的宏,如果用於 DLL 應用程序的實現中,則表示輸出(因為_AFX_EXT被定義,通常是在編譯器的標識參數中指定該選項 /D_AFX_EXT);如果用於使用DLL的應用程序中,則表示輸入(_AFX_EXT沒有定義)。 要輸出整個的類,對類使用_declspec(_DLLexpot);要輸出類的成員函數,則對該函數使用_declspec(_DLLexport)。如:
class AFX_EXT_CLASS CTextDoc : public CDocument { … } extern "C" AFX_EXT_API void WINAPI InitMYDLL();
這幾種方法中,最好采用第三種,方便好用;其次是第一種,如果按順序號輸出,調用效率會高些;最次是第二種。
模塊定義文件(.DEF)
模塊定義文件(.DEF)是一個或多個用於描述 DLL 屬性的模塊語句組成的文本文件,每個DEF文件至少必須包含以下模塊定義語句:
1、DLL與進程、線程之間的關系
2、關於共享數據段
DLL定義的全局變量可以被調用進程訪問;DLL可以訪問調用進程的全局數據。使用同一DLL的每一個進程都有自己的DLL全局變量實例。如果多個線程並發訪問同一變量,則需要使用同步機制;對一個DLL的變量,如果希望每個使用DLL的線程都有自己的值,則應該使用線程局部存儲(TLS,Thread Local Strorage)。 在程序裡加入預編譯指令,或在開發環境的項目設置裡也可以達到設置數據段屬性的目的.必須給這些變量賦初值,否則編譯器會把沒有賦初始值的變量放在一個叫未被初始化的數據段中。