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