跳至內容

動態連結函式庫

出自Taiwan Tongues 台語維基
這是此頁批准,以及是最近的修訂。

動態連結函式庫(英語:Dynamic-link library,縮寫為DLL)是微軟公司佇咧 windows 系統中實現共享函式函式庫概念的一種實作方式。遮的函式庫函式的副檔名是 ` . DLL `、` . OCX `(包含 ActiveX 控制的函式庫)抑是講 ` . DRV `(舊式的系統驅動程式)。

所謂動態連結,就是共一寡定定會共享的程式碼(靜態連結的 OBJ 程式庫)落去共做 DLL 檔,做執行檔呼叫甲 DLL 檔內的函式的時,Windows 作業系統才會共 DLL 檔載入記憶體內,DLL 檔本身的結構就是會當執行檔,當程式有需求時函式才來進行連結。用動態連結方式,記持體浪費的情形將會當大幅降低。靜態連結函式庫是直接連結到執行檔。

DLL 的檔案格式佮視窗 EXE 檔案仝款—— 也就是講,等仝三十二位元視窗的可移植執行檔案(PE)佮十六位元視窗的 New Executable(NE)。 做為 EXE 格式,DLL 會當包括原始碼、資料佮資源的濟款組合。

佇咧較廣泛的意義頂懸講,任何仝款檔案格式的電腦檔案攏會當講號做資源 DLL。按怎 DLL 的例有副檔名做 ` ICL ` 的圖示函式庫、副檔名做 ` FON ` 和 ` FOT ` 的字型檔案。

背景

DLL 的頭先目的是節約應用程式所需要的磁碟佮記憶體空間。佇一个傳統的非共享函式庫內底,一部份程式碼簡單的附加甲呼叫的程式上。若兩个程式呼叫仝一个子程式,伊就會出現兩份彼段號碼。相反,誠濟套用共享的程式碼會當切分著一个 DLL 中,佇硬碟上存為一个檔案,佇記持體中使用一个實例(instance)。 DLL 的廣泛套用予得早期的視窗會當佇緊張的記持體條件下執行。

DLL 提供了如模組化這樣的共享函式庫的普通好處。模組化允准干焦更改幾个應用程式共享使用的一个 DLL 中的程式碼佮資料無需要更改應用程式是家己。這款模組化的基本形式凡勢 Microsoft Office、Microsoft Visual Studio、甚至 Microsoft Windows 家己大的應用程式來使用較絚鬥的修補程式和服務包。

模組化的另外一个好處是外掛程式的通用介面使用。單位的介面允准舊的模組佮新的模組仝款會當佮以早的應用程式執行的時陣無縫地整合甲做伙,毋免對應用程式本身做任何更改。這款的動態擴充的思想佇咧 ActiveX 中發揮到甲極致。

就算講遮爾濟的優點,使用 DLL 嘛有一个缺點:DLL 地獄,也就是幾个應用程式咧使用仝一个共享 DLL 函式庫發生版本的衝突。按呢的衝突會當透過將無仝版本的問題 DLL 囥到應用程式所在的資料篋仔毋是囥到系統資料來解決;猶毋過,按呢欲抵消共享 DLL 節約的空間。目前,Microsoft . NET 欲解決 DLL hell 問題當做家己的目標,伊允准仝一个共享函式庫的無仝版本並列共存(WinSxS)。 因為現代的電腦有夠激烈的吸碟空間佮記持體,這嘛會當做一个合理的實現方法。

技術

記持體管理

佇咧 Win 三十二中間,DLL 檔案按照片段(sections)進行組織。每一个片段有伊家己的內容,若會當寫抑是唯讀、可執行(程式碼)或者是講不可執行(資料)等咧。這是 section 會當分兩種,一个是佮絕對址定址無關係的,所以會用佇加處理程序公用;另外一个是佮絕對位址定址有關的,這著愛由逐个處理程序有家己的副本專用。sections 的這款二分類,咧編譯 DLL 彼个時陣就已經由編譯器、連結器予標註好矣。所以咧裝入 DLL 時,裝入器知影佗一寡 sections 佇記持體實體位址空間只需要有一份,予足濟个處理程序共享(對映到逐个處理程序的記持體邏輯位址空間,所以邏輯位址會當無仝款); 佗幾種 sections 著愛是處理程序使用家己的專用副本。

嘛會當佇咧程式編譯的時陣透過編譯選項 / section 的 S ( Shared ) 內容,顯式指定佗一个節是跨處理的程序共享的。預設的情況下,DLL 的資料節攏是寫時複製(COW)。

具體講,DLL 裝入的時需要考慮之下的情形無:

一 . 局部變數—— 逐个執行緒攏有家己的棧,DLL 內部的局部變數隨所在函式予人執行佇各自執行緒的呼叫棧上開闢儲存空間。 二 . 全域變數一 . DLL 內部定義的全域變數一 . const 全域變數—— 放入去 const 節中,但是毋是逐个處理程序共享;因為處理程序載入 DLL 時會初初化唯讀全域變數的值,這个值由可能是依賴佇咧所在的處理程序,如 DLL 的函式佇該處理程序當中的邏輯位址。 二 . 非 const 全域變數—— 放入逐个處理程序逐个專用的 data 節中。即 DLL 裝入去逐个處理程序複製一份家己專用的 DLL 的 data 節。猶毋過,對一个處理程序內濟濟个執行緒並行存取這款處理程序空間全域變數,猶原有執行緒安全的問題。比如講,佇一个 COM 的 DLL 載入一个處理程序的空間了後,該處理程序濟濟的執行可能會存取該 COM 函式庫的 COM 東西。為此,Windows 佮 COM 引入執行緒「套間」( apartment ) 技術。一个處理程序內底,應用程式佮載入的各個 DLL 分屬於無仝款的 Module,若是 DLL 使用所在 Module 的全域變數,譬如講動態連結 MFC 的 regular dll 咧存取家己的 MFC 全域變數的時陣,應該明確聲明。 二 . 存取 DLL 以外定義的全域變數—— 使用間址技術,佇咧 DLL 的 data 節中用一个指標資料類型的記憶體空間內面儲存一个外部全域變數的位址。 三 . 函式呼叫一 . 搧叫 DLL 內部定義的函式。這毋是問題。 二 . 搧叫 DLL 外部定義的函式。比如講,DLL 內底呼叫一个外部函式 foo ( )。這乎 foo 函式來處理程序一中可能實現為「四捨五入」,佇咧處理程序二中實現為「硩規下」。 所以呼叫外部函式是各一个處理程序私用的代誌。解決辦法是使用間址的技術,佇咧 data 節中用一个「函式指標」資料類型的記憶體空間內底囥這款外部函式的入口位址。 四 . 跳轉指令一 . DLL 內部跳轉,毋是問題二 . 跳甲 DLL 外部,解決同齊寫三更二

DLL 碼段通常予人使用這 DLL 的所有處理程序所共享。若程式碼段所占占去的實體記持體予人收去,伊的內容就會予人放棄,後壁若需要就直接對 DLL 檔案重新載入。

佮程式碼段無仝,DLL 的資料段通常是私有的;也就是講,逐个使用 DLL 的處理程序攏有家己的 DLL 資料副本。作為選擇,資料段會當設定做伙來享,允准透過這个共享記憶體區域進行程間通訊。猶毋過,因為使用者權限袂當套用著這个共享 DLL 記持體,這將產生一个安全漏洞;也就是一个處理程序會當破壞共享資料,這會致使其他的共享處理程序異常。比如講,使用客數號的處理程序將可能透過這種方式破壞其他執行佇特權數號的處理程序。這佇咧 DLL 中避免使用共享片段的一个重要原因。

當 DLL 被如 UPX 按呢一个可執行 packer 壓縮時,伊的所有的程式碼段攏標記為會當讀寫並且是非共享的。會當讀寫的程式碼段,袂輸私有資料段,是逐个處理程序私有的並且予頁面檔案備份。按呢乎,壓縮 DLL 將同時增加記憶體佮吸碟空間銷孝,所以共享 DLL 應當避免使用壓縮 DLL。

符號解析佮結合

DLL 輸出的逐个函式攏由一个數字序號唯一標識,嘛會當由會當選的名標識。仝款,DLL 引入的函式嘛會當由序號或者是名字標識。對著內底的函式來講,干焦輸出序號的情形是真捷看著。對大多數的視窗 API 函式來講名是無仝視窗版本之間保留無變的;序號有可能會發生變化。按呢乎,咱袂當根據序號參照視窗 API 函式。

按照序號參照函式並無一定比按照名參照函式效能閣較好:DLL 輸出表是按照名排列的,所以對半走揣會當用來佇咧這个表中根據名來走揣這个函式。另外一方面,干焦線性揣才會當用佇咧根據序號來揣函式的。

將一个可執行檔結束到一个特定版本的 DLL 嘛是有可能的,這也就是講,會當佇咧編譯的時陣解析輸入函式(imported functions)的地址。對著縖結的輸入函式,連結工具儉了輸入函式結束的 DLL 的時間內底撞佮校驗佮。咧執行的時陣 Windows 檢查敢是當咧使用仝款版本的函式庫,若是,Windows 共踅過處理輸入函式;若無若函式庫佮縖結的函式庫無仝,Windows 共按照正常的方式來處理輸入函式。

結合的會當執行檔若執行佇咧佮𪜶編譯所用的環境仝款,函式呼叫將會較緊,若是佇一个無仝的環境𪜶就等仝款正常的呼叫,所以結輸入函式無任何的缺點。比如講,所有的標準 Windows 應用的程式攏結合到𪜶各人的 Windows 發佈版本的系統 DLL。將一个應用程式輸入函式結著伊的目的環境的好機會是佇應用程式安裝的過程。

執行的時通知 DLL 處理程序 / 執行緒載入

處理程序 / 執行緒載入去的時陣,會當透過 DllMain 函式通知 DLL 相關的資訊,提供對應處理的機會。

動態連結函式庫搜揣順序

對於 Windows,載入動態連結函式庫的時:

  • 若記持體中已經有同 module 名的 DLL,除非講 DLL redirection 抑是 manifest,若無直接就用記持體中這个 DLL 毋閣搜揣。
  • 若是 DLL 名屬於當前 Windows 版本的 Known DLL,著愛用 Known DLL。單仔看著 HKEY \ _ LOCAL \ _ MACHINE \ SYSTEM \ CurrentControlSet \ Control \ Session Manager \ KnownDLLs .
  • 若是 DLL 有依賴 DLL,作業系統按預設標準規則根據 module 名搜揣依賴 DLL。就算第一啦 DLL 指定全路徑。

Windows Desktop 應用程式的 DLL 標準搜揣序大:

一 . 應用程式所在 kha-tá-lok-guh; 二 . 系統 kha-tá-lok-guh。GetSystemDirectory 函式來回應該 kha-tá-lok-guh。 三 . 十六位的元系統 kha-tá-lok-guh; 四 . Windows kha-tá-lok-guh。使用 GetWindowsDirectory 函式來回應該 kha-tá-lok-guh。 五 . 當前(做工課)kha-tá-lok-guh; 六 . 環境變數 PATH 中列出的 kha-tá-lok-guh。

若是 SafeDllSearchMode 予人禁,著以前 kha-tá-lok-guh 成做第二个予人搜揣的 kha-tá-lok-guh。

Windows Desktop 應用程式的 DLL 替代搜揣的序大。這包括兩款情形:

  • LoadLibraryEx 函式使用參數 LOAD \ _ WITH \ _ ALTERED \ _ SEARCH \ _ PATH:LoadLibraryEx 函式毋是用來自應用程式所在 kha-tá-lok-guh 開始,咧是咧參數的 lpFileName(即欲載入的 dll)所在 kha-tá-lok-guh 開始搜揣所有的依賴 DLL。
  • 用 SetDllDirectory 函式修改搜素序。

當所有 DLL 攏完成搜揣,愛開始執行 DLL 初初的時陣,替代誌揣起來結束,恢復標準搜揣序大。

執行的時陣顯式連結

對一个處理程序,逐予人載入的 DLL 佇處理程序的 PEB 中有一个參照計數器,佇當前處理程序當中每濟一改載入該計數器便加一。` LoadLibrary ` 佮 ` FreeLibrary ` 指令影響每一个行程內含的計數器;動態連結是無影響。因此藉呼叫 ` FreeLibrary ` 共記持體徙掉 DLL 是足重要的。一个行程會使對伊家己的 VAS 註銷此計數器。這數器變做零時,這乎 DLL 對當前處理程序當中提掉,並無意味會對實體記憶體內底移除,因為其他的處理程序可能使用這 DLL。

DLL 檔案會當執行的時陣來使用 ` LoadLibrary `(抑是講 ` LoadLibraryEx `)API 函式進行顯式呼叫,這个的過程微軟簡單共號做執行的時陣動態呼叫。若咧講 API 的參數中指明了函式庫檔案的全路徑,若是無欲閣揣這个函式庫檔案。API 函式 ` GetProcAddress ` 欲揣物件有某名稱的輸出函式、` FreeLibrary ` 徙掉 DLL(實際上是參照計數器減一)。 遮的函式類似 POSIX 標準 API 中的 ` dlopen `、` dlsym `、和 ` dlclose `。

注意微軟簡單稱為「執行的時陣動態連結」的執行的時隱式連結,若袂當揣著連結的 DLL 檔案,Windows 欲提示一个錯誤訊息並且呼叫應用程式失敗(確確實講是袂當建立處理程序)。 應用程式開發人員袂當透過編譯連結來處理這款欠缺 DLL 檔案的隱式連結問題,而且佇閣較改矣實現了後閣愛重新編譯連結規个程式—— 預設的增量連結模式會一直保留著彼條欠缺的函式參照,干焦重新編譯連結才會當去掉。相反,雖然顯式連結的程式碼量增加,啊但開發人員有機會提供一个完善的出錯處理機制。

執行的時陣顯式連結的過程佇咧所有語言內底攏是仝款的,因為伊依賴佇咧 Windows API 毋是語言結構。只要一種語言會當呼叫伊上述的 ` LoadLibrary ` 等函式,就會當執行執行的時顯示連結。

Win 十六下的 DLL

十六位元 Windows,所有處理程序共享仝一个記憶體位址空間。若一个 DLL 予人加一个處理程序使用,伊干焦予人載入去記憶體內底一改,干焦一份資料節(data segment)。 也就是講,DLL 是系統全域毋是處理程序內底的;處理程序袂當得著 DLL 的一份獨立的副本。彼每一个 DLL 佇記憶內底干焦一份實例(instance)。

若一个 EXE 檔案同時執行濟擺,若佇記持內底干焦彼號 EXE 的一套唯讀複製(如程式碼或者是資源), 但是這 EXE 的每一个處理程序攏有家己的一套資料段,即有多份的實例(instance)。 實際上,處理程序的實例控制代碼就是 data segment(囥佇處理程序的全域變數)的記持體起始的地址。

模組(module)是講一个硬碟檔案,會當予人載入記持的內底。模組控制代碼是一个資料結構,表示這个硬碟檔案的各部份(section)出自佗位,敢是已經載入到記憶內底。

所以處理程序干焦會當用實例控制代碼標識,袂當用模組控制代碼標識。

Win 三十二的處理程序使用家己專用的邏輯記憶體空間。處理程序的全域變數不再是跨處理程序邊界可見的。實例控制代碼佮模組控制代碼仝款,攏是指向模組載入後的記持體基位址。按呢規定實際上嘛相容矣 Win 十六時實例控制代碼佮模組控制代碼的含義。

資源載入

EXE 和 DLL 攏有其實家己的資源(若是對話方塊資源), 而且遮的資源的 ID 可能會重複,預設使用 EXE 的資源。若需要載入、使用 DLL 中的資源,需要透過 DLL 載入後的實例控制代碼(HINSTANCE)來揣著 DLL 的資源。

應用程式處理程序本身佮喝咻的每一个 DLL 模組攏有一个全域唯一的 HINSTANCE 控制代碼,𪜶代表著講 EXE 抑是 DLL 模組佇咧處理程序邏輯位址空間內底的起始位址。處理程序本身的模組控制代碼一般為零 x 四十五,而且 DLL 模組預設載入去到位址做零 x 一千石。若程式同齊載入真濟个 DLL,著逐个 DLL 模組攏會有無仝的 HINSTANCE。

幾種會當行的辦法:

方法一 :

AFX \ _ MANAGE \ _ STATE ( AfxGetStaticModuleState ( ) ) ; 著愛做介面函式的第一條語句。功能是佇咧棧頂懸來建立一个 AFX \ _ MAINTAIN \ _ STATE 二類別的實例,利用其建構函式和解構函式對 \ _ afxThreadState . GetData ( ) 所指向記持體儲存塊的模組狀態(AFX \ _ MODULE \ _ STATE 類型)設定現場(AfxGetModuleState 函式倒轉來的值)佮恢復現場。

方法二:

方法三:

編譯器佮語言考慮

Delphi

佇原始檔的頭使用關鍵詞 ` library ` 毋是 ` program `,佇檔案的尾仔輸出函式來使用 ` exports ` 排列。

Delphi 無需要 ` LIB ` 檔案以對 DLL 中輸入函式。為著連結一个 DLL,佇函式聲明中使用關鍵詞 ` external `。

微軟 Visual Basic 佮 . Net 框架

佇咧 Visual Basic(VB)中干焦支援執行的時陣連結;但是除了使用 ` LoadLibrary ` 和 ` GetProcAddress ` 這兩个 API 函式以外,允准使用輸入函式的聲明來引入 DLL 函式,若揣無 ` DLL ` 檔案,VB 將產生一个執行無偌久。開發人員會用得揣異常並且進行適當的處理。

Visual Basic 六按呢較老的語言,干焦會當呼 ` _ _ stdcall ` 呼叫約定修飾的函式,並需要佇聲明 DLL 的展現函式的時陣會使用 Alias,抑若無就會出現佇咧 DLL 中揣袂著函式入口點的錯誤。比如講:

嘛是會當用 def 檔案來定義 dll 輸出函式的名。。 另外需要注意,VB 六呼叫的 dll,若閣依賴其他的 dll,若按呢遮的間接依賴的 dll 著愛佇咧 VB 六程式的 dll 搜揣路草頂懸,抑無會報無法度揣著(予直接依賴的)dll 的錯誤。

為著相容,` Declare Function ` 的方式一直沿用著 VB . net。猶毋過佇 . net 中,平台呼叫提供一種新的聲明 DLL 中的輸出函式的方式,透過 ` System . Runtime . InteropServices . DllImportAttribute ` 提供。比如講:

佇仝為 . net 語言的 C # 中,這是干焦有的處理方式。當然代管 C / C + + 就毋免受此限制,見下節。

C 和 C + +

微軟 Visual C + +(MSVC)提供了真濟標準 C + + 的擴充,伊允准直接佇 C + + 程式碼中將函式(類、資料變數)聲明為輸入抑是輸出;這種做法已經予人 Windows 平台頂懸其他的 C 和 C + + 編譯器採納,包括講 Windows 平台頂的 GCC。這款擴充佇函式聲明前使用 ` _ _ declspec ` 內容:

  • ` _ declspec ( dllexport ) ` 用於在 DLL 原始檔中聲明欲輸出的 C + + 類別、函式佮資料。
  • ` _ declspec ( dllimport ) ` 用佇咧外部程式聲明由 DLL 輸出的 C + + 類別、函式佮資料。足濟狀況下無使用 ` _ _ declspec ( dllimport ) ` 也會當正確編譯程式碼。毋過使用 ` _ _ declspec ( dllimport ) ` 予編譯器會當生閣較好的程式碼,因為伊會當確定函式敢有存在 DLL 中,這愛予伊迒 DLL 邊界的函式呼叫時編譯器會當生跳過間接定地址級別的程式碼。需要特別注意的是,著愛使用 ` _ _ declspec ( dllimport ) ` 才會當匯入 DLL 中輸出的變數。比如講,DLL 輸出一个 C + + 類別,該類有一个靜態變數,按呢佇外部檔案使用這个類的時著愛用 ` _ _ declspec ( dllimport ) ` 聲明。

若是遵對 C 號名規範(naming convention)外部名,𪜶必須佇咧 C + + 行式碼中聲明為 ` extern " C " ` 以避免𪜶使用 C + + 號名規範。若使用 dll 的語言(如 Fortran、Visual Basic 六)袂當辨識 C 號名規範,遐爾仔需要採取辦法指出佇咧 DLL 中輸出函式的名。嘛會當透過 DEF 檔案來定義輸出函式的名佮序號。抑是使用如下的連結指令來制定 dll 輸出函式名:

` ` `

  1. pragma comment ( linker , " / export : add=@ add @ 八 " )

` ` `

除了使用 ` _ _ declspec ` 內容定義輸入輸出函式以外,𪜶嘛會當列佇專案 ` DEF ` 檔案的 IMPORT 抑是講 EXPORTS 部份。` DEF ` 檔案毋是由編譯器來進行處理,是由連結器來的根據生的 DLL 檔案內底輸出函式的名佮順序(ordinal number), 按呢乎 DEF 檔案就毋是 C / C + + 特有的,其他的語言寫的程式若想欲編譯做 DLL 嘛會使用 DEF。

DLL 的編譯共生做 ` DLL ` 和 ` LIB ` 兩个檔案。` LIB ` 檔案予人叫做輸入函式庫(import library), 咧編譯的時陣為著呼叫 DLL 的程式提供「柱仔」(stub)實際上是間接跳轉到執行的時載入的 DLL 的對應的函式上閣繼續執行。這種透過輸入函式庫來使用 DLL 的方式,佇程式執行的時啟動處理程序的時陣就會自動(隱式)載入所有用著的 DLL。另外一種使用 DLL 的方式是透過 ` LoadLibrary `(抑是講 ` LoadLibraryEx `)API 函式進行顯式載入 DLL,用 ` GetProcAddress ` API 函式透過函式名稱取得載入了後的記持體位址、透過 ` FreeLibrary ` 徙掉 DLL。

` DLL ` 一般來講著愛下佇咧 PATH 環境變數、預設系統路經或者是使用伊的程式所佇這个路徑三个的一个之內。COM 侍服器 DLL 使用 regsvr 三十二 . exe 註冊,伊將 DLL 的路徑佮全域唯一身份(GUID)記錄佇登錄檔中。應用程式會當透過佇咧登錄檔內底來走揣 GUID、揣著伊的路草對而且使用這个 DLL。

編程實例

建立 DLL 輸出函式

下跤的例展示佮特定語言相關的對 DLL 輸出符號表的方法。

Delphi

C 抑是 C + +

使用 DLL 輸入

下跤的例展示佮特定語言相關的欲按怎佇咧編譯時連結 DLL 輸入符號表的方法。

Delphi

C 抑是 C + +

執行的時陣使用顯式呼叫

下跤的例展示按怎使用無仝語言特有的 WIN 三十二 API 結進行執行的時呼叫佮連結。

Microsoft Visual Basic

C 抑是 C + +

組件物件模型

組件物件模型(COM)將 DLL 概念擴充到物件導向程式設計。物件會當對另外一个處理程序呼叫抑是講佇另外一台機器上執行。COM 物件有一个唯一的 GUID 並且會當實現強大的後台以簡化如 Visual Basic 和 ASP 按怎 GUI 前台套用。𪜶嘛會使用手稿語言編程。COM 物件的建立佮使用比 DLL 閣較複雜。

參見

  • Dependency Walker , 顯示 DLL 和 EXE 檔案出和參照函式的工具
  • Dynamic Library
  • Library Linking ( Computer Science )
  • 連結器
  • 載入器
  • 目的碼
  • Shared Library
  • 靜態連結函式庫
  • DLL 地獄

備註

外部連結

  • Dll Download 自由的 Windows 佮第三方軟體擴展中用著的 Dll 下載
  • \ _ \ _ declspec C + + Language Reference on MSDN
  • dllexport , dllimport on MSDN
  • Dynamic-Link Libraries on MSDN
  • Dynamic-Link Library Functions on MSDN
  • Microsoft Portable Executable and Common Object File Format Specification
  • Win 三十二 DLL www . functionx . com 上的 DLL 製作佮使用教程
  • Delay Load Dlls Error Recovery on www . codemaestro . com .
  • Dll Download and Repair on www . mydll . org

參考文獻

  • Hart , Johnson . _ Windows System Programming Third Edition _ . Addison-Wesley , 兩千空五 . ISBN 空九三百二十一撨二四五千六百一十九九鼻空
  • Rector , Brent et al . _ Win 三十二 Programming _ . Addison-Wesley Developers Press , 一千九百九十七 . ISBN 空九二百空一四六五三千四百九十二孵九 .