WIKI使用導(dǎo)航
站長(zhǎng)百科導(dǎo)航
站長(zhǎng)專題
- 網(wǎng)站推廣
- 網(wǎng)站程序
- 網(wǎng)站賺錢
- 虛擬主機(jī)
- cPanel
- 網(wǎng)址導(dǎo)航專題
- 云計(jì)算
- 微博營(yíng)銷
- 虛擬主機(jī)管理系統(tǒng)
- 開(kāi)放平臺(tái)
- WIKI程序與應(yīng)用
- 美國(guó)十大主機(jī)
動(dòng)態(tài)數(shù)據(jù)交換
動(dòng)態(tài)鏈接庫(kù)英文為DLL,是Dynamic Link Library 的縮寫(xiě)形式,DLL是一個(gè)包含可由多個(gè)程序同時(shí)使用的代碼和數(shù)據(jù)的庫(kù),DLL不是可執(zhí)行文件。動(dòng)態(tài)鏈接提供了一種方法,使進(jìn)程可以調(diào)用不屬于其可執(zhí)行代碼的函數(shù)。
函數(shù)的可執(zhí)行代碼位于一個(gè)DLL中,該DLL包含一個(gè)或多個(gè)已被編譯、鏈接并與使用它們的進(jìn)程分開(kāi)存儲(chǔ)的函數(shù)。DLL還有助于共享數(shù)據(jù)和資源。多個(gè)應(yīng)用程序可同時(shí)訪問(wèn)內(nèi)存中單個(gè)DLL 副本的內(nèi)容。
概述[ ]
DLL是微軟公司在微軟視窗操作系統(tǒng)中實(shí)現(xiàn)共享函數(shù)庫(kù)概念的一種實(shí)作方式。這些庫(kù)函數(shù)的擴(kuò)展名是.DLL、.OCX(包含ActiveX控制的庫(kù))或者.DRV(舊式的系統(tǒng)驅(qū)動(dòng)程序)。
所謂動(dòng)態(tài)鏈接,就是把一些經(jīng)常會(huì)共用的代碼(靜態(tài)鏈接的OBJ程序庫(kù))制作成DLL檔,當(dāng)可執(zhí)行文件調(diào)用到DLL檔內(nèi)的函數(shù)時(shí),windows操作系統(tǒng)才會(huì)把DLL檔加載存儲(chǔ)器內(nèi),DLL檔本身的結(jié)構(gòu)就是可可執(zhí)行文件,當(dāng)程序需求函數(shù)才進(jìn)行鏈接。通過(guò)動(dòng)態(tài)鏈接方式,存儲(chǔ)器浪費(fèi)的情形將可大幅降低。
DLL的文檔格式與視窗EXE文檔一樣——也就是說(shuō),等同于32位視窗的可移植執(zhí)行文檔(PE)和16位視窗的New Executable(NE)。作為EXE格式,DLL可以包括源代碼、數(shù)據(jù)和資源的多種組合。在更廣泛的意義上說(shuō),任何同樣文檔格式的電腦文件都可以稱作資源DLL。這樣的DLL的例子有有擴(kuò)展名ICL為的圖標(biāo)庫(kù)、擴(kuò)展名為FON和FOT的字體文檔。
通過(guò)使用 DLL,程序可以實(shí)現(xiàn)模塊化,由相對(duì)獨(dú)立的組件組成。例如,一個(gè)計(jì)帳程序可以按模塊來(lái)銷售。可以在運(yùn)行時(shí)將各個(gè)模塊加載到主程序中(如果安裝了相應(yīng)模塊)。因?yàn)槟K是彼此獨(dú)立的,所以程序的加載速度更快,而且模塊只在相應(yīng)的功能被請(qǐng)求時(shí)才加載。
此外,可以更為容易地將更新應(yīng)用于各個(gè)模塊,而不會(huì)影響該程序的其他部分。例如,您可能具有一個(gè)工資計(jì)算程序,而稅率每年都會(huì)更改。當(dāng)這些更改被隔離到 DLL 中以后,您無(wú)需重新生成或安裝整個(gè)程序就可以應(yīng)用更新。
DLL的產(chǎn)生[ ]
在DOS時(shí)代,程序員是通過(guò)編寫(xiě)程序來(lái)達(dá)到預(yù)期的目的的,每實(shí)現(xiàn)一個(gè)目的就需要編寫(xiě)一個(gè)程序,這樣下去,簡(jiǎn)單的還好,要是復(fù)雜的程序話,那是既浪費(fèi)時(shí)間,又浪費(fèi)青春。于是聰明的程序員們想出了一個(gè)辦法,把的實(shí)現(xiàn)一定功能的程序模塊存放在一個(gè)文件當(dāng)中,以API函數(shù)形式存放在dll當(dāng)中,當(dāng)編寫(xiě)程序的時(shí)候,需要用到這個(gè)功能,那么直接從這個(gè)文件當(dāng)中調(diào)用就可以了,于是就出現(xiàn)了dll——?jiǎng)討B(tài)連接庫(kù)。
這樣,程序員把一些模塊壓入dll文件之后,在要運(yùn)行程序的時(shí)候只需要調(diào)用動(dòng)態(tài)鏈接庫(kù)就可以了,而并不需要把dll加載到內(nèi)存中。通過(guò)使用 DLL,程序可以實(shí)現(xiàn)模塊化,由相對(duì)獨(dú)立的組件組成。
特征[ ]
DLL 的優(yōu)點(diǎn)[ ]
- 擴(kuò)展了應(yīng)用程序的特性;
- 可以用許多種編程語(yǔ)言來(lái)編寫(xiě);
- 簡(jiǎn)化了軟件項(xiàng)目的管理;
- 有助于節(jié)省內(nèi)存;
- 有助于資源共享;
- 有助于應(yīng)用程序的本地化;
- 有助于解決平臺(tái)差異;
- 可以用于一些特殊的目的。windows使得某些特性只能為DLL所用
內(nèi)存管理[ ]
在Win32中,DLL文檔按照片段(sections)進(jìn)行組織。每個(gè)片段有它自己的屬性,如可寫(xiě)或是只讀、可執(zhí)行(代碼)或者不可執(zhí)行(數(shù)據(jù))等等。
DLL代碼段通常被使用這個(gè)DLL的進(jìn)程所共享;也就是說(shuō)它們?cè)谖锢韮?nèi)存中占據(jù)一個(gè)地方,并且不會(huì)出現(xiàn)在頁(yè)面文檔中。如果代碼段所占據(jù)的物理內(nèi)存被收回,它的內(nèi)容就會(huì)被放棄,后面如果需要的話就直接從DLL文檔重新加載。
與代碼段不同,DLL的數(shù)據(jù)段通常是私有的;也就是說(shuō),每個(gè)使用DLL的進(jìn)程都有自己的DLL數(shù)據(jù)副本。作為選擇,數(shù)據(jù)段可以設(shè)置為共享,允許通過(guò)這個(gè)共享內(nèi)存區(qū)域進(jìn)行進(jìn)程間通信。但是,因?yàn)橛脩魴?quán)限不能應(yīng)用到這個(gè)共享DLL內(nèi)存,這將產(chǎn)生一個(gè)安全漏洞;也就是一個(gè)進(jìn)程能夠破壞共享數(shù)據(jù),這將導(dǎo)致其它的共享進(jìn)程異常。例如,一個(gè)使用訪客賬號(hào)的進(jìn)程將可能通過(guò)這種方式破壞其它運(yùn)行在特權(quán)賬號(hào)的進(jìn)程。這是在DLL中避免使用共享片段的一個(gè)重要原因。
當(dāng)DLL被如UPX這樣一個(gè)可執(zhí)行的packer壓縮時(shí),它的所有代碼段都標(biāo)記為可以讀寫(xiě)并且是非共享的??梢宰x寫(xiě)的代碼段,類似于私有數(shù)據(jù)段,是每個(gè)進(jìn)程私有的并且被頁(yè)面文檔備份。這樣,壓縮DLL將同時(shí)增加內(nèi)存和磁盤(pán)空間消耗,所以共享DLL應(yīng)當(dāng)避免使用壓縮DLL。
符號(hào)解析和綁定[ ]
DLL輸出的每個(gè)函數(shù)都由一個(gè)數(shù)字序號(hào)唯一標(biāo)識(shí),也可以由可選的名字標(biāo)識(shí)。同樣,DLL引入的函數(shù)也可以由序號(hào)或者名字標(biāo)識(shí)。對(duì)于內(nèi)部函數(shù)來(lái)說(shuō),只輸出序號(hào)的情形很常見(jiàn)。對(duì)于大多數(shù)視窗API函數(shù)來(lái)說(shuō)名字是不同視窗版本之間保留不變的;序號(hào)有可能會(huì)發(fā)生變化。這樣,我們不能根據(jù)序號(hào)引用視窗API函數(shù)。
按照序號(hào)引用函數(shù)并不一定比按照名字引用函數(shù)性能更好:DLL輸出表是按照名字排列的,所以對(duì)半查找可以用來(lái)在在這個(gè)表中根據(jù)名字查找這個(gè)函數(shù)。另外一方面,只有線性查找才可以用于根據(jù)序號(hào)查找函數(shù)。
將一個(gè)可執(zhí)行文件綁定到一個(gè)特定版本的DLL也是可能的,這也就是說(shuō),可以在編譯時(shí)解析輸入函數(shù)(imported functions)的地址。對(duì)于綁定的輸入函數(shù),連結(jié)工具保存了輸入函數(shù)綁定的DLL的時(shí)間戳和校驗(yàn)和。在運(yùn)行時(shí)Windows檢查是否正在使用同樣版本的庫(kù),如果是的話,Windows將繞過(guò)處理輸入函數(shù);否則如果庫(kù)與綁定的庫(kù)不同,Windows將按照正常的方式處理輸入函數(shù)。
綁定的可執(zhí)行文件如果運(yùn)行在與它們編譯所用的環(huán)境一樣,函數(shù)調(diào)用將會(huì)較快,如果是在一個(gè)不同的環(huán)境它們就等同于正常的調(diào)用,所以綁定輸入函數(shù)沒(méi)有任何的缺點(diǎn)。例如,所有的標(biāo)準(zhǔn)Windows應(yīng)用程序都綁定到它們各自的Windows發(fā)布版本的系統(tǒng)DLL。將一個(gè)應(yīng)用程序輸入函數(shù)綁定到它的目的環(huán)境的好機(jī)會(huì)是在應(yīng)用程序安裝的過(guò)程。
運(yùn)行時(shí)顯式鏈接[ ]
對(duì)每個(gè)DLL來(lái)說(shuō),Windows存儲(chǔ)了一個(gè)全局計(jì)數(shù)器,每多一個(gè)進(jìn)程使用便多額外一個(gè)。LoadLibrary與FreeLibrary指令影響每一個(gè)進(jìn)程內(nèi)含的計(jì)數(shù)器;動(dòng)態(tài)連結(jié)則不影響。因此借由調(diào)用FreeLibrary多次,從存儲(chǔ)器反加載一DLL是很重要的。一個(gè)進(jìn)程可以從它自己的VAS注銷此計(jì)數(shù)器。
DLL文檔能夠在運(yùn)行時(shí)使用LoadLibrary(或者LoadLibraryEx)API函數(shù)進(jìn)行顯式調(diào)用,這個(gè)的過(guò)程微軟簡(jiǎn)單地稱為運(yùn)行時(shí)動(dòng)態(tài)調(diào)用。API函數(shù)GetProcAddress根據(jù)查找輸出名稱符號(hào)、FreeLibrary卸載DLL。這些函數(shù)類似于POSIX標(biāo)準(zhǔn)API中的dlopen、dlsym、和dlclose。
注意微軟簡(jiǎn)單稱為運(yùn)行時(shí)動(dòng)態(tài)鏈接的運(yùn)行時(shí)隱式鏈接,如果不能找到鏈接的DLL文檔,Windows將提示一個(gè)錯(cuò)誤消息并且調(diào)用應(yīng)用程序失敗。應(yīng)用程序開(kāi)發(fā)人員不能通過(guò)編譯鏈接來(lái)處理這種缺少DLL文檔的隱式鏈接問(wèn)題。另外一方面,對(duì)于顯式鏈接,開(kāi)發(fā)人員有機(jī)會(huì)提供一個(gè)完善的出錯(cuò)處理機(jī)制。
運(yùn)行時(shí)顯式鏈接的過(guò)程在所有語(yǔ)言中都是相同的,因?yàn)樗蕾囉赪indows API而不是語(yǔ)言結(jié)構(gòu)。
DLL的分類[ ]
微軟的Visual C++支持三種DLL,它們分別是Non-MFC Dll(非MFC動(dòng)態(tài)庫(kù))、Regular Dll(常規(guī)DLL)、Extension Dll(擴(kuò)展DLL)。
Non-MFCDLL(非MFC動(dòng)態(tài)庫(kù))[ ]
這種動(dòng)態(tài)鏈接庫(kù)指的是不用MFC的類庫(kù)結(jié)構(gòu),直接用C語(yǔ)言寫(xiě)的DLL,其導(dǎo)出的函數(shù)是標(biāo)準(zhǔn)的C接口,能被非MFC或MFC編寫(xiě)的應(yīng)用程序所調(diào)用。如果建立的DLL不需要使用MFC,那么應(yīng)該建立Non-MFCDLL,因?yàn)槭褂肕FC會(huì)增大用戶庫(kù)的大小,從而浪費(fèi)用戶的磁盤(pán)和內(nèi)存空間。
RegularDLL(常規(guī)DLL)[ ]
這種動(dòng)態(tài)鏈接庫(kù)和下述的ExtensionDll一樣,是用MFC類庫(kù)編寫(xiě)的,它的一個(gè)明顯的特點(diǎn)是在源文件里有一個(gè)繼承CWinApp的類(注意:此類DLL雖然從CWinApp派生,但沒(méi)有消息循環(huán)),被導(dǎo)出的函數(shù)是C函數(shù)、C++類或者C++成員函數(shù)(注意不要把術(shù)語(yǔ)C++類與MFC的微軟基礎(chǔ)C++類相混淆),調(diào)用常規(guī)DLL的應(yīng)用程序不必是MFC應(yīng)用程序,只要是能調(diào)用類C函數(shù)的應(yīng)用程序就可以,它們可以是在VisualC++、Delphi、VisualBasic、BorlandC等編譯環(huán)境下利用DLL開(kāi)發(fā)應(yīng)用程序。常規(guī)DLL又可細(xì)分成靜態(tài)鏈接到MFC和動(dòng)態(tài)鏈接到MFC兩種:
- 靜態(tài)連接到MFC的動(dòng)態(tài)連接庫(kù)只被VC的專業(yè)般和企業(yè)版所支持。該類DLL里的輸出函數(shù)可以被任意Win32程序使用,包括使用MFC的應(yīng)用程序。輸出函數(shù)有如下形式:
extern"C"EXPORTYourExportedFunction();
如果沒(méi)有extern"C"修飾,輸出函數(shù)僅僅能從C++代碼中調(diào)用。
- 動(dòng)態(tài)鏈接到MFC的常規(guī)DLL里的輸出函數(shù)可以被任意Win32程序使用,包括使用MFC的應(yīng)用程序。所有從DLL輸出的函數(shù)應(yīng)該以如下語(yǔ)句開(kāi)始:
AFX_MANAGE_STATE(AfxGetStaticModuleState())
此語(yǔ)句用來(lái)正確地切換MFC模塊狀態(tài)。
ExtensionDll(擴(kuò)展DLL)[ ]
這種動(dòng)態(tài)鏈接庫(kù)是使用MFC的動(dòng)態(tài)鏈接版本所創(chuàng)建的,并且它只被用MFC類庫(kù)所編寫(xiě)的應(yīng)用程序所調(diào)用。例如你已經(jīng)創(chuàng)建了一個(gè)從MFC的CtoolBar類的派生類用于創(chuàng)建一個(gè)新的工具欄,為了導(dǎo)出這個(gè)類,你必須把它放到一個(gè)MFC擴(kuò)展的DLL中。
擴(kuò)展DLL和常規(guī)DLL不一樣,它沒(méi)有一個(gè)從CWinApp繼承而來(lái)的類的對(duì)象,所以,開(kāi)發(fā)人員必須在DLL中的DllMain函數(shù)添加初始化代碼和結(jié)束代碼。與常規(guī)DLL相比,擴(kuò)展的DLL有如下不同點(diǎn):
- 它沒(méi)有一個(gè)從CWinApp派生的對(duì)象;
- 它必須有一個(gè)DLLMain函數(shù);
- DLLMain調(diào)用AfxInitExtensionModule函數(shù),必須檢查該函數(shù)的返回值,如果返回0,DLLMmain也返回0;
- 如果它希望輸出CRuntimeClass類型的對(duì)象或者資源(Resources),則需要提供一個(gè)初始化函數(shù)來(lái)創(chuàng)建一個(gè)CDynLinkLibrary對(duì)象。并且,有必要把初始化函數(shù)輸出;
- 使用擴(kuò)展DLL的MFC應(yīng)用程序必須有一個(gè)從CWinApp派生的類,而且,一般在InitInstance里調(diào)用擴(kuò)展DLL的初始化函數(shù)。
DLL的調(diào)用[ ]
動(dòng)態(tài)鏈接庫(kù)的調(diào)用可以分為兩種:一種是隱式調(diào)用,一種是顯示調(diào)用。
隱式的調(diào)用[ ]
這種調(diào)用方式需要把產(chǎn)生動(dòng)態(tài)連接庫(kù)時(shí)產(chǎn)生的.LIB文件加入到應(yīng)用程序的工程中,在使用DLL中的函數(shù)時(shí),只須說(shuō)明一下后就可以直接通過(guò)函數(shù)名調(diào)用DLL的輸出函數(shù),調(diào)用方法和程序內(nèi)部其他的函數(shù)是一樣的。隱式調(diào)用不需要調(diào)用LoadLibrary()和FreeLibrary()。程序員在建立一個(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ò)隱式調(diào)用方式編譯生成應(yīng)用程序時(shí),應(yīng)用程序中的調(diào)用函數(shù)與LIB文件中導(dǎo)出符號(hào)相匹配,這些符號(hào)或標(biāo)識(shí)號(hào)被寫(xiě)入到生成的EXE文件中。LIB文件中也包含了對(duì)應(yīng)的DLL文件名(但不是完全的路徑名),鏈接程序也將其存儲(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)鏈接。所有被應(yīng)用程序調(diào)用的DLL文件都會(huì)在應(yīng)用程序EXE文件加載時(shí)被加載在到內(nèi)存中。
顯式調(diào)用[ ]
這種調(diào)用方式是指在應(yīng)用程序中用LoadLibrary或MFC提供的AfxLoadLibrary顯式的將自己所做的動(dòng)態(tài)連接庫(kù)調(diào)進(jìn)來(lái),并指定DLL的路徑作為參數(shù)。LoadLibary返回HINSTANCE參數(shù),應(yīng)用程序在調(diào)用GetProcAddress函數(shù)時(shí)使用這一參數(shù)。當(dāng)完成對(duì)動(dòng)態(tài)鏈接庫(kù)的導(dǎo)入以后,再使用GetProcAddress()獲取想要引入的函數(shù),該函數(shù)將符號(hào)名或標(biāo)識(shí)號(hào)轉(zhuǎn)換為DLL內(nèi)部的地址,之后就可以象使用本應(yīng)用程序自定義的函數(shù)一樣來(lái)調(diào)用此引入函數(shù)了。在應(yīng)用程序退出之前,應(yīng)該用FreeLibrary或MFC提供的AfxFreeLibrary釋放動(dòng)態(tài)連接庫(kù)。
編程實(shí)例[ ]
創(chuàng)建DLL輸出函數(shù)[ ]
下面的例子展示了與特定語(yǔ)言相關(guān)的從DLL輸出符號(hào)表的方法。
Delphi:
library Example; // Function that adds two numbers function AddNumbers(a, b: Double): Double; cdecl; begin AddNumbers := a + b end; // Export this function exports AddNumbers; // DLL initialization code: no special handling needed begin end.
C 或 C++
#include <windows.h> // Export this function extern "C" __declspec(dllexport) double AddNumbers(double a, double b); // DLL initialization function BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { return TRUE; } // Function that adds two numbers double AddNumbers(double a, double b) { return a + b; }
使用DLL輸入[ ]
下面的例子展示了與特定語(yǔ)言相關(guān)的如何在編譯時(shí)鏈接DLL輸入符號(hào)表的方法。
Delphi
program Example; {$APPTYPE CONSOLE} // Import function that adds two numbers function AddNumbers(a, b: Double): Double; cdecl; external 'Example.dll'; var result: Double; begin result := AddNumbers(1, 2); Writeln('The result was: ', result) end.
C 或 C++
#include <windows.h> #include <stdio.h> // Import function that adds two numbers extern "C" __declspec(dllimport)double AddNumbers(double a, double b); int main(int argc, char **argv) { double result = AddNumbers(1, 2); printf("The result was: %f\n", result); return 0; }
運(yùn)行時(shí)使用顯式調(diào)用[ ]
下面的例子先是如何使用不同語(yǔ)言特有的WIN32 API綁定進(jìn)行運(yùn)行時(shí)的調(diào)用和鏈接。
Microsoft Visual Basic
Option Explicit Declare Function AddNumbers Lib "Example.dll" (ByVal a As Double, ByVal b As Double) As Double Sub Main() Dim Result As Double Result = AddNumbers(1, 2) Debug.Print "The result was: " & Result End Sub
C 或 C++
#include <windows.h> #include <stdio.h> // DLL function signature typedef double (*importFunction)(double, double); int main(int argc, char **argv) { importFunction addNumbers; double result; // Load DLL file HINSTANCE hinstLib = LoadLibrary("Example.dll"); if (hinstLib == NULL) { printf("ERROR: unable to load DLL\n"); return 1; } // Get function pointer addNumbers = (importFunction)GetProcAddress(hinstLib, "AddNumbers"); if (addNumbers == NULL) { printf("ERROR: unable to find DLL function\n"); return 1; } // Call function. result = addNumbers(1, 2); // Unload DLL file FreeLibrary(hinstLib); // Display result printf("The result was: %f\n", result); return 0; }