一般的,在介紹Windows編程的書中講述DLL的有關知識較多,而介紹MFC的書則比較少地提到。即使使用MFC來編寫動態鏈接庫,對於初步接觸DLL的程序員來說,了解DLL的背景知識是必要的。另外,MFC提供了新的手段來幫助編寫DLL程序。所以,本節先簡潔的介紹有關概念。
DLL的背景知識
靜態鏈接和動態鏈接
當前鏈接的目標代碼(.obj)如果引用了一個函數卻沒有定義它,鏈接程序可能通過兩種途徑來解決這種從外部對該函數的引用:
靜態鏈接
鏈接程序搜索一個或者多個庫文件(標准庫.lib),直到在某個庫中找到了含有所引用函數的對象模塊,然後鏈接程序把這個對象模塊拷貝到結果可執行文件(.exe)中。鏈接程序維護對該函數的所有引用,使它們指向該程序中現在含有該函數拷貝的地方。
動態鏈接
鏈接程序也是搜索一個或者多個庫文件(輸入庫.lib),當在某個庫中找到了所引用函數的輸入記錄時,便把輸入記錄拷貝到結果可執行文件中,產生一次對該函數的動態鏈接。這裡,輸入記錄不包含函數的代碼或者數據,而是指定一個包含該函數代碼以及該函數的順序號或函數名的動態鏈接庫。
當程序運行時,Windows裝入程序,並尋找文件中出現的任意動態鏈接。對於每個動態鏈接,Windows裝入指定的DLL並且把它映射到調用進程的虛擬地址空間(如果沒有映射的話)。因此,調用和目標函數之間的實際鏈接不是在鏈接應用程序時一次完成的(靜態),相反,是運行該程序時由Windows完成的(動態)。
這種動態鏈接稱為加載時動態鏈接。還有一種動態鏈接方式下面會談到。
動態鏈接的方法
鏈接動態鏈接庫裡的函數的方法如下:
加載時動態鏈接(Load_time dynamic linking)
如上所述。Windows搜索要裝入的DLL時,按以下順序:
應用程序所在目錄→當前目錄→Windows SYSTEM目錄→Windows目錄→PATH環境變量指定的路徑。
運行時動態鏈接(Run_time dynamic linking)
程序員使用LoadLibrary把DLL裝入內存並且映射DLL到調用進程的虛擬地址空間(如果已經作了映射,則增加DLL的引用計數)。首先,LoadLibrary搜索DLL,搜索順序如同加載時動態鏈接一樣。然後,使用GetProcessAddress得到DLL中輸出函數的地址,並調用它。最後,使用FreeLibrary減少DLL的引用計數,當引用計數為0時,把DLL模塊從當前進程的虛擬空間移走。
輸入庫(.lib):
輸入庫以.lib為擴展名,格式是COFF(Common object file format)。COFF標准庫(靜態鏈接庫)的擴展名也是.lib。COFF格式的文件可以用dumpbin來查看。
輸入庫包含了DLL中的輸出函數或者輸出數據的動態鏈接信息。當使用MFC創建DLL程序時,會生成輸入庫(.lib)和動態鏈接庫(.dll)。
輸出文件(.exp)
輸出文件以.exp為擴展名,包含了輸出的函數和數據的信息,鏈接程序使用它來創建DLL動態鏈接庫。
映像文件(.map)
映像文件以.map為擴展名,包含了如下信息:
模塊名、時間戳、組列表(每一組包含了形式如section::offset的起始地址,長度、組名、類名)、公共符號列表(形式如section::offset的地址,符號名,虛擬地址flat address,定義符號的.obj文件)、入口點如section::offset、fixup列表。
lib.exe工具
它可以用來創建輸入庫和輸出文件。通常,不用使用lib.exe,如果工程目標是創建DLL程序,鏈接程序會完成輸入庫的創建。
更詳細的信息可以參見MFC使用手冊和文檔。
鏈接規范(Linkage Specification )
這是指鏈接采用不同編程語言寫的函數(Function)或者過程(Procedure)的鏈接協議。MFC所支持的鏈接規范是“C”和“C++”,缺省的是“C++”規范,如果要聲明一個“C”鏈接的函數或者變量,則一般采用如下語法:
#if defined(__cplusplus)
extern "C"
{
#endif
//函數聲明(function declarations)
…
//變量聲明(variables declarations)
#if defined(__cplusplus)
}
#endif
所有的C標准頭文件都是用如上語法聲明的,這樣它們在C++環境下可以使用。