程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 第十章-動態鏈接庫編程(一)(3)

第十章-動態鏈接庫編程(一)(3)

編輯:Delphi

10.2.2 調用DLLs

有兩種方法可用於調用一個儲存在DLLs中的過程。

1.靜態調用或顯示裝載

使用一個外部聲明子句,使DLLs在應用程序開始執行前即被裝入。例如: 

function Instr(SourceStr : PChar;Check : Char); Integer; far; external 'UseStr';

使用這種方法,程序無法在運行時間裡決定DLLs的調用。假如一個特定的DLLs在運行時無法使用,則應用程序將無法執行。

2.動態調用或隱式裝載

使用Windows API函數LoadLibray和GetProcAddress可以實現在運行時間裡動態裝載DLLs並調用其中的過程。

若程序只在其中的一部分調用DLLs的過程,或者程序使用哪個DLLs, 調用其中的哪個過程需要根據程序運行的實際狀態來判斷,那麼使用動態調用就是一個很好的選擇。

使用動態調用,即使裝載一個DLLs失敗了,程序仍能繼續運行。 

10.2.3 靜態調用

在靜態調用一個DLLs中的過程或函數時,external指示增加到過程或函數的聲明語句中。被調用的過程或函數必須采用遠調用模式。這可以使用far過程指示或一個{$F +}編譯指示。

Delphi全部支持傳統Windows動態鏈接庫編程中的三種調用方式,它們是:

● 通過過程/函數名

● 通過過程/函數的別名

● 通過過程/函數的順序號 

通過過程或函數的別名調用,給用戶編程提供了靈活性,而通過順序號(Index)調用可以提高相應DLL的裝載速度。 

10.2.4 動態調用 

10.2.4.1 動態調用中的API函數 

動態調用中使用的Windows API函數主要有三個,即:Loadlibrary,GetProcAddress和Freelibrary。

1.Loadlibrary: 把指定庫模塊裝入內存

語法為: 

function Loadlibrary(LibFileName: PChar): THandle; 

LibFileName指定了要裝載DLLs的文件名,如果LibFileName沒有包含一個路徑,則Windows按下述順序進行查找:

(1)當前目錄;

(2)Windows目錄(包含win.com的目錄)。函數GetWindowDirectory返回這一目錄的路徑;

(3)Windows系統目錄(包含系統文件如gdi.exe的目錄)。函數GetSystemDirectory返回這一目錄的路徑;

(4)包含當前任務可執行文件的目錄。利用函數GetModuleFileName可以返回這一目錄的路徑;

(5)列在PATH環境變量中的目錄;

(6)網絡的映象目錄列表。

如果函數執行成功,則返回裝載庫模塊的實例句柄。否則,返回一個小於HINSTANCE_ERROR的錯誤代碼。錯誤代碼的意義如下表: 

表10.2 Loadlibrary返回錯誤代碼的意義

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

錯誤代碼         意        義

——————————————————————————————————————

  0 系統內存不夠,可執行文件被破壞或調用非法

  2 文件沒有被發現

  3 路徑沒有被發現

  5 企圖動態鏈接一個任務或者有一個共享或網絡保護錯

  6 庫需要為每個任務建立分離的數據段

  8 沒有足夠的內存啟動應用程序

10 Windows版本不正確

  11 可執行文件非法。或者不是Windows應用程序,或者在.EXE映

   像中有錯誤

  12 應用程序為一個不同的操作系統設計(如OS/2程序)

13 應用程序為MS DOS4.0設計

  14 可執行文件的類型不知道

  15 試圖裝載一個實模式應用程序(為早期Windows版本設計)

16 試圖裝載包含可寫的多個數據段的可執行文件的第二個實例

  19 試圖裝載一個壓縮的可執行文件。文件必須被解壓後才能被裝裁

  20 動態鏈接庫文件非法

  21 應用程序需要32位擴展

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

假如在應用程序用Loadlibrary調用某一模塊前,其它應用程序已把該模塊裝入內存,則Loadlibrary並不會裝載該模塊的另一實例,而是使該模塊的“引用計數”加1。 

2.GetProcAddress:撿取給定模塊中函數的地址

語法為: 

function GetProcAddress(Module: THandle; ProcName: PChar): TFarProc; 

Module包含被調用的函數庫模塊的句柄,這個值由Loadlibrary返回。如果把Module設置為nil,則表示要引用當前模塊。

ProcName是指向含有函數名的以nil結尾的字符串的指針,或者也可以是函數的次序值。如果ProcName參數是次序值,則如果該次序值的函數在模塊中並不存在時,GetProcAddress仍返回一個非nil的值。這將引起混亂。因此大部分情況下用函數名是一種更好的選擇。如果用函數名,則函數名的拼寫必須與動態鏈接庫文件EXPORTS節中的對應拼寫相一致。

如果GetProcAddress執行成功,則返回模塊中函數入口處的地址,否則返回nil。

3.Freelibrary:從內存中移出庫模塊

語法為: 

procedure Freelibrary(Module : THandle); 

Module為庫模塊的句柄。這個值由Loadlibrary返回。

由於庫模塊在內存中只裝載一次,因而調用Freelibrary首先使庫模塊的引用計數減一。如果引用計數減為0,則卸出該模塊。

每調用一次Loadlibrary就應調用一次FreeLibray,以保證不會有多余的庫模塊在應用程序結束後仍留在內存中。 

10.2.4.2 動態調用舉例 

對於動態調用,我們舉了如下的一個簡單例子。系統一共包含兩個編輯框。在第一個編輯框中輸入一個字符串,而後在第二個編輯框中輸入字符。如果該字符包含在第一個編輯框的字符串中,則標簽框顯示信息:“位於第n位。”,否則顯示信息:“不包含這個字符。”。如圖是程序的運行界面。

輸入檢查功能的實現在Edit2的OnKeyPress事件處理過程中,程序清單如下。 

procedure TForm1.Edit2KeyPress(Sender: TObject; var Key: Char);

var

order: Integer;

txt: PChar;

PFunc: TFarProc;

Moudle: THandle;

begin

Moudle := Loadlibrary('c:\dlls\example.dll');

if Moudle > 32 then

begin

Edit2.text := '';

Pfunc := GetProcAddress(Moudle,'Instr');

txt := StrAlloc(80);

txt := StrPCopy(txt,Edit1.text);

Order := TInstr(PFunc)(txt,Key);

if Order = -1 then

Label1.Caption := '不包含這個字符 '

else

Label1.Caption := '位於第'+IntToStr(Order+1)+'位';

end;

Freelibrary(Moudle);

end;

在利用GetProcAddess返回的函數指針時,必須進行強制類型轉換: 

Order := TInstr(PFunc)(text,Key);

TInStr是一個定義好了的函數類型: 

type

TInStr = function(Source: PChar;Check: Char): Integer; 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved