天天看點

Delphi中動态連結庫兩種調用方式的比較

一、動态連結庫的概念

動态連結庫(Dynamic Link Library,縮寫為DLL)是一個可以被其它應用程式共享的程式子產品,其中封裝了一些可以被共享的例程和資源。動态連結庫檔案的擴充名一般是dll, 也有可能是drv、sys和fon,它和可執行檔案(exe)非常類似,差別在于DLL中雖然包含了可執行代碼卻不能單獨執行,而應由Windows應用 程式直接或間接調用。

動态連結是相對于靜态連結而言的。所謂靜态連結是指把要調用的函數或者過程連結到可執行檔案 中,成為可執行檔案的一部分。換句話說,函數和過程的代碼就在程式的exe檔案中,該檔案包含了運作時所需的全部代碼。當多個程式都調用相同函數時,記憶體 中就會存在這個函數的多個拷貝,這樣就浪費了寶貴的記憶體資源。而動态連結所調用的函數代碼并沒有被拷貝到應用程式的可執行檔案中去,而是僅僅在其中加入了 所調用函數的描述資訊(往往是一些重定位資訊)。僅當應用程式被裝入記憶體開始運作時,在Windows的管理下,才在應用程式與相應的DLL之間建立連結 關系。當要執行所調用DLL中的函數時,根據連結産生的重定位資訊,Windows才轉去執行DLL中相應的函數代碼。

一般情況下,如果一個應用程式使用了動态連結庫,Win32系統保證記憶體中隻有DLL的一份 複制品,這是通過記憶體映射檔案實作的。DLL首先被調入Win32系統的全局堆棧,然後映射到調用這個DLL的程序位址空間。在Win32系統中,每個進 程擁有自己的32位線性位址空間,如果一個DLL被多個程序調用,每個程序都會收到該DLL的一份映像。與16位Windows不同,在Win32中 DLL可以看作是每個程序自己的代碼。

二、動态連結庫的優點

1. 共享代碼、資源和資料

使用DLL的主要目的就是為了共享代碼,DLL的代碼可以被所有的Windows應用程式共 享。

2. 隐藏實作的細節

DLL中的例程可以被應用程式通路,而應用程式并不知道這些例程的細節。

3. 拓展開發工具如Delphi的功能

由于DLL是與語言無關的,是以可以建立一個DLL,被C++、VB或任何支援動态連結庫的 語言調用。這樣如果一種語言存在不足,就可以通過通路另一種語言建立的DLL來彌補。

三、動态連結庫的實作方法

1. Load-time Dynamic Linking

這種用法的前提是在編譯之前已經明确知道要調用DLL中的哪幾個函數,編譯時在目标檔案中隻 保留必要的連結資訊,而不含DLL函數的代碼;當程式執行時,利用連結資訊加載DLL函數代碼并在記憶體中将其連結入調用程式的執行空間中,其主要目的是便 于代碼共享。

2. Run-time Dynamic Linking

這種方式是指在編譯之前并不知道将會調用哪些DLL函數,完全是在運作過程中根據需要決定應 調用哪個函數,并用LoadLibrary和GetProcAddress動态獲得DLL函數的入口位址。

四、DLL的兩種調用方式在Delphi中的比較

編寫DLL的目的是為了輸出例程供其他程式調用,是以在DLL的工程檔案中要把輸出的例程用 Exports關鍵字引出。在調用DLL的應用程式中,需要聲明用到的DLL中的方法,聲明格式要和DLL中的聲明一樣。通路DLL中的例程有靜态調用和 動态調用兩種方式。靜态調用方式就是在單元的Interface部分用External訓示字列出要從DLL中引入的例程;動态調用方式就是通過調用 Windows的API包括LoadLibrary函數、GetProcAddress函數以及FreeLibrary函數動态的引入DLL中的例程。

靜态調用方式所需的代碼較動态調用方式所需的少,但存在着一些不足,一是如果要加載的DLL 不存在或者DLL中沒有要引入的例程,這時候程式就自動終止運作;二是DLL一旦加載就一直駐留在應用程式的位址空間,即使DLL已不再需要了。動态調用 方式就可解決以上問題,它在需要用到DLL的時候才通過LoadLibrary函數引入,用完後通過FreeLibrary函數從記憶體中解除安裝,而且通過調 GetProcAddress函數可以指定不同的例程。最重要的是,如果指定的DLL出錯,至多是API調用失敗,不會導緻程式終止。以下将通過具體的實 例說明說明這調用方式的使用方法。

1. 靜态調用方式

示例程式建立了一個DLL,其中僅包含一個求兩個整數的和的函數,在主程式中輸入兩個整數, 通過調用該DLL,即可求出兩個整數的和。

該DLL的程式代碼如下:

library AddNum;

            uses

            SysUtils,

            Classes;

            {$R *.res}

            function AddNumber(Num1,Num2:integer):integer;stdcall; //定義求和函數

             begin

              result:=Num1+Num2;

             end;

              exports

              AddNumber; //引出求和函數

             begin

            end.      

主程式在調用該DLL時,首先在interface部分聲明要調用的函數:

function AddNum(Num1,Num2:integer):integer;stdcall;external 'AddNum.dll'

            name 'AddNumber';      

然後在按鈕控件的事件中寫入如下代碼:

procedure TForm1.Button1Click(Sender: TObject);

            var

             Number1,Number2:integer;

             Sum:integer;

            begin

             Number1:=strtoint(Edit1.Text);

             Number2:=strtoint(Edit2.Text);

             Sum:=AddNum(Number1,Number2); //調用求和函數計算結果

             Edit3.Text:=inttostr(Sum);

            end;      

2.動态調用方式

這個示例程式建立了一個顯示日期的DLL,其中包含一個窗體。

程式中定義了一個ShowCalendar函數,傳回在這個窗體中設定的日期。函數定義如 下:

function ShowCalendar(AHandle: THandle; ACaption: String): TDateTime;

            var

             DLLForm: TDLLForm;

            begin

             Application.Handle := AHandle;

             DLLForm := TDLLForm.Create(Application); //建立并顯示窗體

             try

              DLLForm.Caption := ACaption;

              DLLForm.ShowModal; //顯示方式為模式化

              Result := DLLForm.calDLLCalendar.CalendarDate; //傳回設定日期

             finally

              DLLForm.Free; //用完後解除安裝該窗體

             end;

            end;      

在DLL的工程檔案中用exports ShowCalendar; 語句引出該函數。下面通過一個簡單的應用程式測試一下該DLL檔案。建立一個工程檔案,在窗體中放置一個Label控件和一個按鈕控件,在按鈕控件的 OnClick事件中編寫如下代碼:

procedure TMainForm.Button1Click(Sender: TObject);

            var

             OneHandle : THandle; //定義一個句柄變量

            begin

             OneHandle := LoadLibrary('Clendar.dll'); //動态載入DLL,并傳回其句柄

             try

              if OneHandle <> 0 then //如果載入成功則擷取ShowCalendar函數的位址

               @ShowCalendar := GetProcAddress(OneHandle, 'ShowCalendar');

               if not (@ShowCalendar = nil) then

                //如果找到該函數則在主窗體的Label1中顯示DLL窗體中設定的日期

                Label1.Caption := DateToStr(ShowCalendar(Application.Handle, Caption))

               else

                RaiseLastWin32Error;

             finally

              FreeLibrary(OneHandle); //調用完畢收回DLL占用的資源

             end;

            end;      

從以上程式中可以看到DLL的動态調用方式比靜态調用方式的優越之處。DLL例程在用到時才 被調入,用完後就被解除安裝,大大減少了系統資源的占用。在調用LoadLibrary函數時可以明确指定DLL的完整路徑,如果沒有指定路徑,運作時首先查 找應用程式載入的目錄,然後是Windows系統的System目錄和環境變量Path設定的路徑。

五、結束語

由于動态連結庫可以實作代碼和資源的共享,大大減少系統資源的占用,是以在當今的應用程式開 發中起着非常重要的作用。Delphi是現今流行的應用軟體開發工具,本文就如何在Delphi中使用動态連結庫給出了一定程度上的闡述。

繼續閱讀