Windows的動態鏈接庫原理
動態鏈接庫(DLLs)是從C語言函數庫和Pascal庫單元的概念發展而來的所有的C語言標准庫函數都存放在某一函數庫中同時用戶也可以用LIB程序創建自己的函數庫在鏈接應用程序的過程中鏈接器從庫文件中拷貝程序調用的函數代碼並把這些函數代碼添加到可執行文件中這種方法同只把函數儲存在已編譯的OBJ文件中相比更有利於代碼的重用
但隨著Windows這樣的多任務環境的出現函數庫的方法顯得過於累贅如果為了完成屏幕輸出消息處理內存管理對話框等操作每個程序都不得不擁有自己的函數那麼Windows程序將變得非常龐大Windows的發展要求允許同時運行的幾個程序共享一組函數的單一拷貝動態鏈接庫就是在這種情況下出現的動態鏈接庫不用重復編譯或鏈接一旦裝入內存Dlls函數可以被系統中的任何正在運行的應用程序軟件所使用而不必再將DLLs函數的另一拷貝裝入內存
動態鏈接庫的工作原理
動態鏈接這幾字指明了DLLs是如何工作的對於常規的函數庫鏈接器從中拷貝它需要的所有庫函數並把確切的函數地址傳送給調用這些函數的程序而對於DLLs函數儲存在一個獨立的動態鏈接庫文件中在創建Windows程序時鏈接過程並不把DLLs文件鏈接到程序上直到程序運行並調用一個DLLs中的函數時該程序才要求這個函數的地址此時Windows才在DLLs中尋找被調用函數並把它的地址傳送給調用程序采用這種方法DLLs達到了復用代碼的極限
動態鏈接庫的另一個方便之處是對動態鏈接庫中函數的修改可以自動傳播到所有調用它的程序中而不必對程序作任何改動或處理
DLLs不僅提供了函數重用的機制而且提供了數據共享的機制任何應用程序都可以共享由裝入內存的DLLs管理的內存資源塊只包含共享數據的DLLs稱為資源文件如Windows的字體文件等
Windows系統的動態鏈接庫
Windows本身就是由大量的動態鏈接庫支持的這包括Windows API函數 ( KRNLxEXEUSEREXEGDIEXE…)各種驅動程序文件各種帶有Fon和Fot 擴展名的字體資源文件等Windows還提供了針對某一功能的專用DLLs如進行DDE編程的ddemldll進行程序安裝的verdll等
雖然在編寫Windows程序時必然要涉及到DLLs但利用Delphi 用戶在大部分時候並不會注意到這一點這一方面是因為Delphi提供了豐富的函數使用戶不必直接去使用Windows API;另一方面即使使用Windows API由於Delphi把API函數和其它Windows DLLs函數重新組織到了幾個庫單元中因而也不必使用特殊的調用格式所以本章的重點放在編寫和調用用戶自定義的DLLs上
使用傳統的Windows編程方法來創建和使用一個DLLs是一件很令人頭痛的事正如傳統的Windows編程方法本身就令人生畏一樣用戶需要對定義文件工程文件進行一系列的修改以適應創建和使用DLLs的需要Delphi的出現在這一方面正如在其它許多方面所做的那樣減輕了開發者的負擔更令人興奮的是Delphi利用DLLs 實現了窗體的重用機制用戶可以將自己設計好的窗體儲存在一個DLLs中在需要的時候可隨時調用它
DLLs的編寫和調用
DLLs的編寫
在Delphi環境中編寫一個DLLs同編寫一個一般的應用程序並沒有太大的區別事實上作為DLLs 主體的DLL函數的編寫除了在內存資源的管理上有所不同外並不需要其它特別的手段真正的區別在工程文件上
在絕大多數情況下用戶幾乎意識不到工程文件的存在因為它一般不顯示在屏幕上如果想查看工程文件則可以打開View菜單選擇Project Source項此時工程文件的代碼就會出現在屏幕的Code Editor(代碼編輯器)中
一般工程文件的格式為
program 工程標題
uses 子句
程序體
而DLLs工程文件的格式為
library 工程標題
uses 子句
exprots 子句
程序體
它們主要的區別有兩點
一般工程文件的頭標用program關鍵字而DLLs工程文件頭標用library 關鍵字不同的關鍵字通知編譯器生成不同的可執行文件用program關鍵字生成的是exe文件而用library關鍵字生成的是dll文件
假如DLLs要輸出供其它應用程序使用的函數或過程則必須將這些函數或過程列在exports子句中而這些函數或過程本身必須用export編譯指令進行編譯
根據DLLs完成的功能我們把DLLs分為如下的三類
完成一般功能的DLLs;
用於數據交換的DLLs;
用於窗體重用的DLLs
這一節我們只討論完成一般功能的DLLs其它內容將在後邊的兩節中討論
編寫一般DLLs的步驟
編寫一般DLLs的步驟如下
利用Delphi的應用程序模板建立一個DLLs程序框架
對於Delphi 的用戶由於沒有DLLs模板因此
()建立一個一般的應用程序並打開工程文件
()移去窗體和相應的代碼單元
()在工程文件中把program改成library移去Uses子句中的Forms並添加適當的庫單元(一般SysUtilsClasses是需要的)刪去begin…end之間的所有代碼
以適當的文件名保持文件此時library後跟的庫名自動修改
輸入過程函數代碼如果過程函數准備供其它應用程序調用則在過程函數頭後加上export 編譯指示
建立exports子句包含供其它應用程序調用的函數和過程名可以利用標准指示 name Indexresident以方便和加速過程/函數的調用
輸入庫初始化代碼這一步是可選的
編譯程序生成動態鏈接庫文件
動態鏈接庫中的標准指示
在動態鏈接庫的輸出部分用到了三個標准指示nameIndexresident
name
name後面接一個字符串常量作為該過程或函數的輸出名如
exports
InStr name MyInstr;
其它應用程序將用新名字(MyInstr)調用該過程或函數如果仍利用原來的名字(InStr)則在程序執行到引用點時會引發一個系統錯誤
Index
Index指示為過程或函數分配一個順序號如果不使用Index指示則由編譯器按順序進行分配
Index後所接數字的范圍為…使用Index可以加速調用過程
resident
使用resident則當DLLs裝入時特定的輸出信息始終保持在內存中這樣當其它應用程序調用該過程時可以比利用名字掃描DLL入口降低時間開銷
對於那些其它應用程序常常要調用的過程或函數使用resident指示是合適的例如
exports
InStr name MyInStr resident;
[] [] [] []
From:http://tw.wingwit.com/Article/program/Delphi/201311/25207.html