褚姝韞 李冀東 王晉
摘要:動態鏈接庫英文為DLL,是Dynamic Link Library 的縮寫形式,DLL是一個包含可由多個程序同時使用的代碼和數據的庫,動態鏈接庫使進程可以調用不屬于其可執行代碼的函數,還有助于共享數據和資源。通過使用 DLL,程序可以實現模塊化,由相對獨立的組件組成,可以更為容易地將更新應用于各個模塊,而不會影響該程序的其他部分,為程序的開發帶來了很大的便利。這篇文章研究了VC下動態鏈接庫的生成和調用。
關鍵詞:動態鏈接庫;DLL;程序;模塊;共享數據
中圖分類號:TP311.52
1 引 言
動態鏈接庫英文為DLL,是Dynamic Link Library 的縮寫形式,DLL是一個包含可由多個程序同時使用的代碼和數據的庫,動態鏈接庫使進程可以調用不屬于其可執行代碼的函數,還有助于共享數據和資源。
當程序使用DLL時,具有的優點如下:
(1) 使用較少的資源
當多個程序使用同一個函數庫時,DLL 可以減少在磁盤和物理內存中加載的代碼的重復量。使前臺的程序得到良好的運行,而且可以大大減少對其它程序的影響。
(2) 推廣模塊式體系結構
DLL有助于促進模塊式程序的開發,這可以幫助您開發要求提供多個語言版本的大型程序或要求具有模塊式體系結構的程序。
(3) 簡化部署和安裝
當 DLL 中的函數需要更新或修復時,部署和安裝 DLL 不要求重新建立程序與該 DLL 的鏈接。此外,如果多個程序使用同一個 DLL,那么多個程序都將從該更新或修復中獲益。當您使用定期更新或修復的第三方 DLL 時,會大大簡化程序的更新步驟。
DLL在程序開發中具有無可替代的作用,本文介紹了DLL的生成和鏈接。
2 動態鏈接庫的生成和鏈接
動態鏈接庫其實就是為應用程序提供服務并具有某一特定功能的函數和類的集合,與它相對的是靜態鏈接庫。
靜態鏈接庫與動態鏈接庫都是共享代碼的方式,如果采用靜態鏈接庫,則無論你愿不愿意,lib中的指令都被直接包含在最終生成的EXE文件中了。但是若使用DLL,該DLL不必被包含在最終EXE文件中,EXE文件執行時可以“動態”地引用和卸載這個與EXE獨立的DLL文件。靜態鏈接庫和動態鏈接庫的另外一個區別在于靜態鏈接庫中不能再包含其他的動態鏈接庫或者靜態庫,而在動態鏈接庫中還可以再包含其他的動態或靜態鏈接庫。
下面簡要的介紹一下在VC下可以生成的動態鏈接庫的分類。
2.1 動態鏈接庫的分類
使用VC模板可以生成以下三種不同類型的DLL:
(1)Win32DLL
只能導出C函數和變量,但可以使用除CObject派生的類。不能在導出函數中建立對話框,因為不能進行模塊環境轉換。
(2)MFC常規DLL
只能導出C函數和變量,但可以使用MFC中所有的類,在使用DLL中的資源時要進行模塊環境轉換,在每個要導出的函數最前面加上
AFX_MANAGE_STATE(AfxGetStaticModuleState()),也可以使用以下人工轉換:HINSTANCE hCurContext=AfxGetResourceHandle();
AfxSetResourceHandle(GetModuleHandle("temp.dll"));//可以使用DLL中的資源了。
HRSRC hRes = FindResource(hCurContext,MAKEINTRESOURCE(129),
RT_DIALOG); //可以創建窗口了
AfxSetResourceHandle(hCurContext);
另外標準C語言中不支持重載,因為C語言的調用協定(__cdecl)生成的代碼中函數名只有一個_(下劃線)做前綴,所有該類型的DLL不能導出重載函數。
(3)MFC擴展DLL
支持C++接口,可以導出C++類,成員函數及重載函數,只支持動態MFC庫。
每一個DLL必須有一個入口點,DllMain是一個缺省的入口函數。dllMain負責初始化(Initialization)和結束(Termination)工作,每當一個新的進程或者該進程的新的線程訪問DLL時,或者訪問DLL的每一個進程或者線程不再使用DLL或者結束時,都會調用DllMain。但是,使用TerminateProcess或TerminateThread結束進程或者線程,不會調用DllMain。
下面以一個簡單的小程序為例,說明動態動態鏈接庫的生成和鏈接。
2.2 動態鏈接庫的生成
動態鏈接庫的生成有兩種方法:
(1)使用導出函數關鍵字_declspec(dllexport)創建MyDll.dll,該動態鏈接庫中有兩個函數,分別用來實現得到兩個數的最大和最小數。
在MyDll.h添加:
extern "C" _declspec(dllexport) int Max(int a, int b);
extern "C" _declspec(dllexport) int Min(int a, int b);
在MyDLL.cpp中添加:
#include"MyDll.h"
int Max(int a, int b)
{……。。//要實現的功能代碼}
int Min(int a, int b)
{…………//要實現的功能代碼}
該動態鏈接庫編譯成功后,打開MyDll工程中的debug目錄,可以看到MyDll.dll、MyDll.lib兩個文件。LIB文件中包含DLL文件名和DLL文件中的函數名等,該LIB文件只是對應該DLL文件的“映像文件”,與DLL文件相比,LIB文件的長度要小的多,在進行隱式鏈接DLL時要用到它。讀者可能已經注意到在MyDll.h中有關鍵字"extern C",它可以使其他編程語言訪問你編寫的DLL中的函數。
(2)用.def文件創建工程MyDll
為了用.def文件創建DLL,請先刪除上個例子創建的工程中的MyDll.h文件,保留MyDll.cpp并在該文件頭刪除#include MyDll.h語句,同時往該工程中加入一個文本文件,命名為MyDll.def,再在該文件中加入如下代碼:
LIBRARY MyDll
EXPORTS
MaxMin
其中LIBRARY語句說明該def文件是屬于相應DLL的,EXPORTS語句下列出要導出的函數名稱。我們可以在.def文件中的導出函數后加@n,如Max@1,Min@2,表示要導出的函數順序號,在進行顯式連時可以用到它。該DLL編譯成功后,打開工程中的Debug目錄,同樣也會看到MyDll.dll和MyDll.lib文件。
2.3 動態鏈接庫的鏈接
應用程序使用DLL可以采用兩種方式:一種是隱式鏈接,另一種是顯式鏈接。在使用DLL之前首先要知道DLL中函數的結構信息。Visual C++6.0在VCin目錄下提供了一個名為Dumpbin.exe的小程序,用它可以查看DLL文件中的函數結構。另外,Windows系統將遵循下面的搜索順序來定位DLL: ① 包含EXE文件的目錄;② 進程的當前工作目錄; ③ Windows系統目錄; ④ Windows目錄;⑤ 列在Path環境變量中的一系列目錄。
(1)隱式鏈接
隱式鏈接就是在程序開始執行時就將DLL文件加載到應用程序當中。實現隱式鏈接很容易,只要將導入函數關鍵字_declspec(dllimport)函數名等寫到應用程序相應的頭文件中就可以了。下面的例子通過隱式鏈接調用MyDll.dll庫中的Min函數。
在創建DllTest.exe文件之前,要先將MyDll.dll和MyDll.lib拷貝到當前工程所在的目錄下面,也可以拷貝到windows的System目錄下。如果DLL使用的是def文件,要刪除TestDll.h文件中關鍵字“extern C”。TestDll.h文件中的關鍵字Progam commit是要Visual C+的編譯器在link時,鏈接到MyDll.lib文件,當然,開發人員也可以不使用#pragma comment(lib,"MyDll.lib")語句,而直接在工程的Setting->Link頁的Object/Moduls欄填入MyDll.lib既可。
(2)顯式鏈接
顯式鏈接是應用程序在執行過程中隨時可以加載DLL文件,也可以隨時卸載DLL文件,這是隱式鏈接所無法作到的,所以顯式鏈接具有更好的靈活性,對于解釋性語言更為合適。不過實現顯式鏈接要麻煩一些。在應用程序中用LoadLibrary或MFC提供的AfxLoadLibrary顯式的將自己所做的動態鏈接庫調進來,動態鏈接庫的文件名即是上述兩個函數的參數,此后再用GetProcAddress()獲取想要引入的函數。自此,你就可以象使用如同在應用程序自定義的函數一樣來調用此引入函數了。在應用程序退出之前,應該用FreeLibrary或MFC提供的AfxFreeLibrary釋放動態鏈接庫。下面是通過顯式鏈接調用DLL中的Max函數的例子。
在通過顯式鏈接調用DLL中的Max函數中使用類型定義關鍵字typedef,定義指向和DLL中相同的函數原型指針,然后通過LoadLibray()將DLL加載到當前的應用程序中并返回當前DLL文件的句柄,然后通過GetProcAddress()函數獲取導入到應用程序中的函數指針,函數調用完畢后,使用FreeLibrary()卸載DLL文件。在編譯程序之前,首先要將DLL文件拷貝到工程所在的目錄或Windows系統目錄下。
使用顯式鏈接應用程序編譯時不需要使用相應的Lib文件。另外,使用GetProcAddress()函數時,可以利用MAKEINTRESOURCE()函數直接使用DLL中函數出現的順序號,如將GetProcAddress(hDLL,"Min")改為GetProcAddress(hDLL, MAKEINTRESOURCE(2))(函數Min()在DLL中的順序號是2),這樣調用DLL中的函數速度很快,但是要記住函數的使用序號,否則會發生錯誤。
3 結 論
本文介紹了VC模板下動態鏈接庫的生成和鏈接,動態鏈接庫使進程可以調用不屬于其可執行代碼的函數,還有助于共享數據和資源,促進模塊化程序的開發,簡化程序更新的步驟,在實際開發中要多運用動態鏈接庫。
參考文獻
[1]宋寶華. VC++動態鏈接庫編程之基本概念[0L]. http://dev.yesky.com/228/2141728.shtml
[2]何鵬飛.WINDOWS動態連接庫技術的探討[J].計算機應用研究,1995,3:18-20.