19.1 DLL和進程的地址空間
DLL通常由一組可供任何應(yīng)用程序的獨立函數(shù)組成鸣个。在DLL中通常沒有用來處理消息循環(huán)或創(chuàng)建窗口的代碼。
DLL只不過是一組源代碼模塊,每個模塊包含一些可供應(yīng)用程序或其他DLL調(diào)用的函數(shù)。
在應(yīng)用程序能夠調(diào)用一個DLL中的函數(shù)之前臀突,必須將該DLL的文件映像映射到調(diào)用進程的地址空間中。
我們可以通過兩種方法來達到這一目的:
1.隱式鏈接 implicit load-timelinking 默認是加載到內(nèi)存中的始終占用內(nèi)存
2.顯示鏈接 explicit run-time linking? 加載時占用內(nèi)存贾漏,釋放了就不占用內(nèi)存了候学。如果該DLL已經(jīng)載入,loadlibrary只是會增加一個引用計數(shù)磕瓷,freelibrary也只是減少引用計數(shù)盒齿,直到引用計數(shù)為0時,DLL才會從內(nèi)存中移除困食。
注意:使用.lib就是隱式鏈接边翁,使用LoadLibrary就是顯式鏈接。
另外硕盹,隱式和顯式只是對編程的時候來講的尽纽,最后產(chǎn)生的可執(zhí)行程序忌卤,都是用loadlibrary載入的汪榔。
兩種方法對于你的程序調(diào)用動態(tài)庫時沒有任何區(qū)別赦拘,只是你在編程時,步驟是不一樣的垛贤。
顯式調(diào)用麻煩點焰坪,但可以沒有相應(yīng)的lib庫;隱式調(diào)用簡單點聘惦,有函數(shù)的聲明就行某饰,但必須有l(wèi)ib庫。
在VC中兩種方式的具體方法:
一、動態(tài)庫的隱式調(diào)用
在vc工程中直接鏈接靜態(tài)輸入庫xxx.lib黔漂,然后即可像調(diào)用其它源文件中的函數(shù)一樣調(diào)用DLL中函數(shù)诫尽。
二、動態(tài)庫的顯式調(diào)用
步驟:
1.創(chuàng)建一個函數(shù)指針炬守,其指針數(shù)據(jù)類型要與調(diào)用的DLL引出函數(shù)相吻合牧嫉。
2.通過win32 API函數(shù)loadlibrary()顯示的調(diào)用DLL,此函數(shù)返回DLL的實例句柄减途。
3.通過win32 API函數(shù)GetProAddress()獲取要調(diào)用的DLL的函數(shù)地址酣藻,把結(jié)果賦給自定義函數(shù)的指針類型。
4.使用函數(shù)指針來調(diào)用DLL函數(shù)鳍置。
5.最后調(diào)用完成后臊恋,通過Win32 API函數(shù)FreeLibrary()釋放DLL函數(shù)。
動態(tài)鏈接庫的加載方式:
一墓捻、隱式鏈接
使用lib印入庫和相關(guān)頭文件,dll文件配合使用
二坊夫、顯式加載
只是使用dll文件即可砖第。
void CDlltextDlg::OnAdd(){
? ? HINSTANCE hnst=LoadLibrary("dll2"); //得到動態(tài)鏈接庫的句柄
? ? typedef int (ADDPROC)(int a ,int b); //定義函數(shù)指針類型
? ? //用函數(shù)指針變量來調(diào)用函數(shù)int *add(int a, int b)表示函數(shù)返回指針值
? ? ADDPROC Add=(ADDPROC)GetProcAddress(hnst, "add"); //得到動態(tài)鏈接庫中add導(dǎo)出函數(shù)的地址
? ? if(!Add){
? ? ? ? MessageBox("獲得函數(shù)地址失敗环凿!")梧兼;
? ? ? ? return;
? ? ? ? }
? ? CString str;
? ? str.Format("5+3=%d",Add(5,3));
? ? MessageBox(str);
? ? FreeLibrary(hnst); //釋放動態(tài)鏈接庫
}
注意:
GetProcAddress也可以采用函數(shù)序號方式調(diào)用,不過最好是用函數(shù)名來獲取函數(shù)地址
ADDPROC Add=(ADDPROC)GetProcAddress(hnst, MAKEINTRESOURCE(1));?
//得到動態(tài)鏈接庫中add導(dǎo)出函數(shù)的地址,第一個函數(shù)表示為add函數(shù)
19.2 縱觀全局
隱式鏈接方式:
先構(gòu)建DLL智听,在構(gòu)建可執(zhí)行模塊羽杰,因為一個可執(zhí)行模塊需要從另一個DLL模塊中導(dǎo)入函數(shù)和變量
構(gòu)建DLL的步驟:
1.創(chuàng)建一個頭文件,包含:我們想要在DLL中導(dǎo)出的函數(shù)原型/結(jié)構(gòu)以及符號到推。為了構(gòu)建該DLL考赛,DLL的所有源文件需要包含這個頭文件。
2.創(chuàng)建C++源文件來實現(xiàn)想要在DLL模塊中導(dǎo)出的函數(shù)和變量莉测。
3.在構(gòu)建DLL模塊時颜骤,編譯器會對每個源文件進行處理并產(chǎn)生一個.obj模塊忍抽。
4.當所有.obj模塊都創(chuàng)建完畢后,鏈接器會將所有.obj模塊的內(nèi)容合并起來,產(chǎn)生一個單獨的DLL映像文件浅辙。該映像文件包含DLL中所有的二進制代碼以及全局靜態(tài)變量。
5.如果鏈接器檢測到DLL的源文件輸出了至少一個函數(shù)或變量泽腮,那么鏈接器還會生成一個.lib文件诊赊。它列出了所有被到處的函數(shù)和變量的符號名遵馆。
構(gòu)建可執(zhí)行模塊的步驟:
1.在所有引用了導(dǎo)出的函數(shù)/變量/數(shù)據(jù)結(jié)構(gòu)或符號的源文件中秆撮,必須包含由DLL的開發(fā)人員所創(chuàng)建的頭文件。
2.創(chuàng)建C++源文件來實現(xiàn)想要在DLL模塊中導(dǎo)出的函數(shù)和變量。當然,代碼可以引用在DLL的頭文件中定義的函數(shù)和變量。
3.在構(gòu)建可執(zhí)行模塊的時候,編譯器會對每個源文件進行處理并產(chǎn)生一個.obj模塊峻汉。
4.當所有.obj模塊都創(chuàng)建完畢后扳埂,鏈接器會將所有.obj模塊的內(nèi)容合并起來柜思,產(chǎn)生一個單獨的可執(zhí)行映像文件赡盘。該映像文件包含可執(zhí)行文件中所有的二進制代碼以及全局靜態(tài)變量号枕。該可執(zhí)行模塊還包含一個導(dǎo)入段(import section),其中列出了所有它需要的DLL模塊的名稱陨享。