1 程序的模塊化
如果你是一個(gè)程序員就會(huì)知道,編程都是需要使用函數(shù)庫(kù)(library)或類庫(kù)的,也就是說(shuō),通常庫(kù)是程序的有機(jī)部分,可以靜態(tài)鏈接(.lib,在使用.lib之前,要在程序源代碼中引用lib對(duì)應(yīng)的頭文件.h并在編譯時(shí)直接加到源文件中)或動(dòng)態(tài)鏈接(DLL,Dynamic Linked Library,程序運(yùn)行時(shí)加載)成為程序的一部分。
在windows中,許多應(yīng)用程序并不是一個(gè)完整的可執(zhí)行文件,它們被分割成一些相對(duì)獨(dú)立的動(dòng)態(tài)鏈接庫(kù),即DLL文件,放置于系統(tǒng)中。當(dāng)我們執(zhí)行某一個(gè)程序時(shí),相應(yīng)的DLL文件就會(huì)被調(diào)用。一個(gè)應(yīng)用程序可使用多個(gè)DLL文件,一個(gè)DLL文件也可能被不同的應(yīng)用程序使用,這樣的DLL文件被稱為共享DLL文件。如在 Windows操作系統(tǒng)中,每個(gè)程序都可以使用該 DLL 中包含的功能來(lái)實(shí)現(xiàn)“打開(kāi)”對(duì)話框。這有助于促進(jìn)代碼重用和內(nèi)存的有效使用。
通過(guò)使用 DLL,程序可以實(shí)現(xiàn)模塊化,由相對(duì)獨(dú)立的組件組成。
MS把所有的API函數(shù)是放在幾個(gè)大的*.LIB中,另外還提供一對(duì)應(yīng)的幾個(gè)*.inc,實(shí)際上它如同*.h頭文件,起到聲明API函數(shù)的目的。
DLL是Dynamic Link Library的縮寫,意為動(dòng)態(tài)鏈接庫(kù)。在Windows中,許多應(yīng)用程序并不是一個(gè)完整的可執(zhí)行文件,它們被分割成一些相對(duì)獨(dú)立的動(dòng)態(tài)鏈接庫(kù),即DLL文件,放置于系統(tǒng)中。當(dāng)我們執(zhí)行某一個(gè)程序時(shí),相應(yīng)的DLL文件就會(huì)被調(diào)用。一個(gè)應(yīng)用程序可有多個(gè)DLL文件,一個(gè)DLL文件也可能被幾個(gè)應(yīng)用程序所共用,這樣的DLL文件被稱為共享DLL文件。DLL文件一般被存放在C:WindowsSystem目錄下。
DLL文件中存放的是各類程序的函數(shù)(子過(guò)程)實(shí)現(xiàn)過(guò)程,當(dāng)程序需要調(diào)用函數(shù)時(shí)需要先載入DLL,然后取得函數(shù)的地址,最后進(jìn)行調(diào)用。使用DLL文件的好處是程序不需要在運(yùn)行之初加載所有代碼,只有在程序需要某個(gè)函數(shù)的時(shí)候才從DLL中取出。另外,使用DLL文件還可以減小程序的體積。
Windows系統(tǒng)平臺(tái)上,你可以將獨(dú)立的程序模塊創(chuàng)建為較小的DLL(Dynamic Linkable Library)文件,并可對(duì)它們單獨(dú)編譯和測(cè)試。在運(yùn)行時(shí),只有當(dāng)EXE程序確實(shí)要調(diào)用這些DLL模塊的情況下,系統(tǒng)才會(huì)將它們裝載到內(nèi)存空間中。這種方式不僅減少了EXE文件的大小和對(duì)內(nèi)存空間的需求,而且使這些DLL模塊可以同時(shí)被多個(gè)應(yīng)用程序使用。Microsoft Windows自己就將一些主要的系統(tǒng)功能以DLL模塊的形式實(shí)現(xiàn)。例如IE中的一些基本功能就是由DLL文件實(shí)現(xiàn)的,它可以被其它應(yīng)用程序調(diào)用和集成。一般來(lái)說(shuō),DLL是一種磁盤文件(通常帶有DLL擴(kuò)展名,是標(biāo)準(zhǔn)win32可執(zhí)行文件-“PE”格式),它由全局?jǐn)?shù)據(jù)、服務(wù)函數(shù)和資源組成,在運(yùn)行時(shí)被系統(tǒng)加載到進(jìn)程的虛擬空間中,成為調(diào)用進(jìn)程的一部分,進(jìn)程中所有線程都可以調(diào)用其中的函數(shù)。如果與其它DLL之間沒(méi)有沖突,該文件通常映射到進(jìn)程虛擬空間的同一地址上。DLL模塊中包含各種導(dǎo)出函數(shù),用于向外界提供服務(wù)。Windows在加載DLL模塊時(shí)將進(jìn)程函數(shù)調(diào)用與DLL文件的導(dǎo)出函數(shù)相匹配。
系統(tǒng)的組策略和注冊(cè)表中,我們可以修改一些鍵值來(lái)優(yōu)化我們的系統(tǒng),并加強(qiáng)操作系統(tǒng)的安全性。可是,對(duì)于限制下載、禁止刪除文件等功能,我們無(wú)法通過(guò)上述的操作來(lái)完成,這只有通過(guò)修改系統(tǒng)DLL文件來(lái)實(shí)現(xiàn)。目前,我們通過(guò)修改系統(tǒng)的DLL文件,可以實(shí)現(xiàn)禁止刪除文件、禁止IE下載、禁止IE另存為、禁止文件打開(kāi)方式等功能。
2 為什么要用DLL
DLL為什么封裝成函數(shù),就能成為系統(tǒng)中大量使用DLL的理由呢?
① 擴(kuò)展應(yīng)用程序
由于DLL能被應(yīng)用程序動(dòng)態(tài)載入內(nèi)存。所以,應(yīng)用程序可以在需要時(shí)才將DLL載入到內(nèi)存中,這讓程序的可維護(hù)性變得很高。比如QQ的視頻功能需要升級(jí),那么負(fù)責(zé)編寫QQ的程序員不必將QQ所有代碼都重寫,只需將視頻功能相關(guān)的DLL文件重寫即可。
② 便于程序員合作
這個(gè)和最終用戶關(guān)系不大,僅供了解。大家都知道編程工具有很多,比如VB、VC、Delphi等,如果好幾個(gè)人合作來(lái)編寫一個(gè)大的程序,那么可能有的人用VB,有的人用VC,每人負(fù)責(zé)的部分所使用的編程語(yǔ)言都不同,究竟放在哪個(gè)編譯器中進(jìn)行編譯呢?這就好比一群來(lái)自各個(gè)國(guó)家的人在共同編寫一篇文章,如果他們所使用的語(yǔ)言都不同,寫出來(lái)的文章怎么可能湊到一起呢?而有了DLL后,可以讓VC程序員寫一個(gè)DLL,然后VB程序員在程序中調(diào)用,無(wú)需為怎么將它們都編譯為一個(gè)單獨(dú)的EXE而發(fā)愁了。
③ 節(jié)省內(nèi)存
如果多個(gè)應(yīng)用程序調(diào)用的是同一個(gè)動(dòng)態(tài)鏈接庫(kù),那么這個(gè)DLL文件不會(huì)被重復(fù)多次裝入內(nèi)存中,而是由這些應(yīng)用程序共享同一個(gè)已載入內(nèi)存的DLL。就好比一個(gè)辦公室中,很少會(huì)為每一個(gè)員工配置一臺(tái)飲水機(jī)的,而是在一個(gè)公共位置放上一個(gè)飲水機(jī),所有需要喝水的職員都可以共用這臺(tái)飲水機(jī),降低了成本又節(jié)約了空間。
④ 共享程序資源
包括剛才提到過(guò)的通用文件對(duì)話框在內(nèi),DLL文件提供了應(yīng)用程序間共享資源的可能。資源可以是程序?qū)υ捒颉⒆址D標(biāo),或者聲音文件等。
⑤ 解決應(yīng)用程序本地化問(wèn)題
在下載了某個(gè)程序的漢化包后,打開(kāi)漢化說(shuō)明,經(jīng)常可以看到用下載包中的DLL文件覆蓋掉程序原來(lái)的DLL,漢化就完成了。這些程序都是將執(zhí)行代碼和應(yīng)用程序界面分開(kāi)編寫了,所以漢化者只需簡(jiǎn)單地將其中和程序界面相關(guān)的DLL漢化并發(fā)布即可。
3 隱式鏈接和顯式鏈接
應(yīng)用程序?qū)牒瘮?shù)與DLL文件中的導(dǎo)出函數(shù)進(jìn)行鏈接有兩種方式:隱式鏈接和顯式鏈接。
隱式鏈接(load-time dynamic linking)是指在應(yīng)用程序中不需指明DLL文件的實(shí)際存儲(chǔ)路徑,程序員不需關(guān)心DLL文件的實(shí)際裝載(由編譯器自動(dòng)完成地址分配)。采用隱式鏈接方式,程序員在建立一個(gè)DLL文件時(shí),鏈接程序會(huì)自動(dòng)生成一個(gè)與之對(duì)應(yīng)的LIB導(dǎo)入文件。該文件包含了每一個(gè)DLL導(dǎo)出函數(shù)的符號(hào)名和可選的標(biāo)識(shí)號(hào),但是并不含有實(shí)際的代碼。LIB文件作為DLL的替代文件被編譯到應(yīng)用程序項(xiàng)目中。當(dāng)程序員通過(guò)靜態(tài)鏈接方式編譯生成應(yīng)用程序時(shí),應(yīng)用程序中的調(diào)用函數(shù)與LIB文件中導(dǎo)出符號(hào)相匹配,這些符號(hào)或標(biāo)識(shí)號(hào)進(jìn)入到生成的EXE文件中。LIB文件中也包含了對(duì)應(yīng)的DLL文件名(但不是完全的路徑名),鏈接程序?qū)⑵浯鎯?chǔ)在EXE文件內(nèi)部。當(dāng)應(yīng)用程序運(yùn)行過(guò)程中需要加載DLL文件時(shí),Windows根據(jù)這些信息發(fā)現(xiàn)并加載DLL,然后通過(guò)符號(hào)名或標(biāo)識(shí)號(hào)實(shí)現(xiàn)對(duì)DLL函數(shù)的動(dòng)態(tài)鏈接。我們使用的大部分系統(tǒng)Dll就是通過(guò)這樣的方式鏈接的。若找不到需要的Dll則會(huì)給出一個(gè)Dll缺少的錯(cuò)誤消息。
顯式鏈接(run-time dynamic linking)與此相反。用戶程序在編譯的時(shí)候并沒(méi)有指明需要哪些Dll,而是在運(yùn)行起來(lái)之后調(diào)用Win32 的LoadLibary()函數(shù),去裝載Dll。若沒(méi)有找到Dll則這個(gè)函數(shù)就會(huì)返回一個(gè)錯(cuò)誤。在用LoadLibary()函數(shù)裝載Dll之后,應(yīng)用程序還需要用GetProcAdress()函數(shù)去獲得Dll輸出函數(shù)的地址。顯式鏈接方式對(duì)于集成化的開(kāi)發(fā)語(yǔ)言比較適合。有了顯式鏈接,程序員就不必再使用導(dǎo)入文件,而是直接調(diào)用Win32 的LoadLibary()函數(shù),并指定DLL的路徑作為參數(shù)。還要說(shuō)明一點(diǎn)的就是Known Dlls就是保證在通過(guò)LoadLibary()去裝載系統(tǒng)Dll的時(shí)候,只從特定的系統(tǒng)目錄去裝載,防止裝載錯(cuò)。裝載的時(shí)候會(huì)去看注冊(cè)表下是否有一樣的注冊(cè)表鍵名。
應(yīng)用程序怎樣找到DLL文件
如果應(yīng)用程序使用LoadLibrary顯式鏈接,那么在這個(gè)函數(shù)的參數(shù)中可以指定DLL文件的完整路徑。如果不指定路徑,或是進(jìn)行隱式鏈接,Windows將遵循下面的搜索順序來(lái)定位DLL:
I 包含EXE文件的目錄,
II 進(jìn)程的當(dāng)前工作目錄,
III Windows系統(tǒng)目錄,
IV Windows目錄,
V 列在Path環(huán)境變量中的一系列目錄。
在Windows上有個(gè)注冊(cè)表鍵值決定了Dll的搜索順序:HKLMSystemCurrentControlSetSessionManagerSafeDllSearchMode。在windows 7,server2003,xp sp2中這個(gè)值為1,在xp,2000 sp4中為0。
1值時(shí)的搜素順序?yàn)椋?.可執(zhí)行文件所在目錄,2.系統(tǒng)目錄windowssystem32,3. 16位系統(tǒng)目錄,4.windows目錄,5.當(dāng)前進(jìn)程目錄。6.環(huán)境變量PATH中的目錄。
0值時(shí)的搜素順序?yàn)椋?.可執(zhí)行文件所在目錄,2. 當(dāng)前進(jìn)程目錄。3.系統(tǒng)目錄windowssystem32,4. 16位系統(tǒng)目錄,5.windows目錄,6.環(huán)境變量PATH中的目錄。
4 DLL的加載與連接
Windows DLL裝入(除ntdll.dll外)和連接是通過(guò)ntdll.dll中一個(gè)函數(shù)LdrInitializeThunk實(shí)現(xiàn)的。先對(duì)LdrInitializeThunk()這個(gè)函數(shù)名作些解釋“Ldr顯然是“Loader”的縮寫。而“Thunk”意為“翻譯”、“轉(zhuǎn)換”、或者某種起著“橋梁”作用的東西。這個(gè)詞在一般的字典中是查不到的,但卻是個(gè)常見(jiàn)于微軟的資料、文檔中術(shù)語(yǔ)。這個(gè)術(shù)語(yǔ)起源于編譯技術(shù),表示一小片旨在獲取某個(gè)地址的代碼,最初用于函數(shù)調(diào)用時(shí)“形參”和“實(shí)參”結(jié)合。后來(lái)這個(gè)術(shù)語(yǔ)有了不少新的特殊含義和使用,但是DLL的動(dòng)態(tài)連接與函數(shù)調(diào)用時(shí)“形實(shí)結(jié)合”確實(shí)有著本質(zhì)的相似。
DLL文件中包含一個(gè)導(dǎo)出函數(shù)表。這些導(dǎo)出函數(shù)由它們的符號(hào)名和稱為標(biāo)識(shí)號(hào)的整數(shù)與外界聯(lián)系起來(lái)。函數(shù)表中還包含了DLL中函數(shù)的地址。當(dāng)應(yīng)用程序加載DLL模塊時(shí)時(shí),它并不知道調(diào)用函數(shù)的實(shí)際地址,但它知道函數(shù)的符號(hào)名和標(biāo)識(shí)號(hào)。動(dòng)態(tài)鏈接過(guò)程在加載的DLL模塊時(shí)動(dòng)態(tài)建立一個(gè)函數(shù)調(diào)用與函數(shù)地址的對(duì)應(yīng)表。如果重新編譯和重建DLL文件,并不需要修改應(yīng)用程序,除非你改變了導(dǎo)出函數(shù)的符號(hào)名和參數(shù)序列。
5 DLL注冊(cè)及為什么需要注冊(cè)?
在系統(tǒng)故障中,有很多都是由于DLL文件丟失、損壞或沒(méi)有注冊(cè)造成的,比如Windows XP的壓縮文件夾功能出現(xiàn)故障就很有可能是系統(tǒng)目錄中的zipfldr.dll沒(méi)有注冊(cè)造成的,這類故障的解決方法也大多是下載一個(gè)對(duì)應(yīng)的DLL并運(yùn)行如下命令:
for%1 in (%windir%system32*.dll)do regsvr32.exe /s %1
很多人不理解為什么要這么做,是不是所有的DLL都能這樣做呢?
其實(shí)系統(tǒng)中有兩種DLL,一種是不需注冊(cè)即可使用的,另一種則是必須經(jīng)過(guò)注冊(cè)才能使用的。就好像一個(gè)臨時(shí)工,和一個(gè)記錄在員工名單上的長(zhǎng)期合同工的區(qū)別一樣。如何才能區(qū)分這兩種DLL呢?方法很簡(jiǎn)單,可以用一個(gè)工具(如Dependency Walker)打開(kāi)這個(gè)DLL,看函數(shù)輸出表,如果其中包含以下兩個(gè)函數(shù)(前者是注冊(cè)DLL,后者是反注冊(cè)DLL),那么就一定是需要注冊(cè)才能使用的DLL了。
DllRegisterServer
DllUnregisterServer
而regsvr32這個(gè)命令,實(shí)際上就是調(diào)用DLL中的這兩個(gè)函數(shù)(“regsvr32 /u DLL文件名”調(diào)用的即為DllUnregisterServer反注冊(cè)函數(shù))。
注冊(cè)與不注冊(cè),.dll文件都在system32下面。不同的是,注冊(cè)了會(huì)在注冊(cè)表中有相應(yīng)信息,同時(shí)載入到了dll緩存,沒(méi)有注冊(cè)信息和進(jìn)緩存就不能使用(開(kāi)機(jī)時(shí)操作系統(tǒng)的一些核心功能再加載到了內(nèi)存,并開(kāi)始在后臺(tái)運(yùn)行,程序、數(shù)據(jù)、模塊都需要進(jìn)入內(nèi)存才能被訪問(wèn)。)。
文件注冊(cè)前和注冊(cè)后都沒(méi)有變化,只是通過(guò)命令把相應(yīng)的信息添加到了注冊(cè)表中。
注冊(cè)文件的命令行是 regsrv32
regsrv32 xxx.dll regsrv32 xxx.dll /u regsvr32 [/u] [/s] [/n] [/i[:cmdline]] dllname 參數(shù)說(shuō)明: /u - 解除服務(wù)器注冊(cè) /s - 無(wú)聲;不顯示消息框 /i - 調(diào)用 DllInstall,給其傳遞一個(gè)可選 [cmdline];跟 /u 一起使用時(shí),卸載 dll /n - 不要調(diào)用 DllRegisterServer;這個(gè)選項(xiàng)必須跟 /i 一起使用
-End-