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