大家在軟體開發時經常會遇到這樣的情況,在不同的開發環境中為實現一些功能相同的過程,由於開發所使用的設計語言不同,因而不得不編寫許多類似的代碼,甚至有時要在同一個開發環境不同項目中重寫代碼,這就造成了很大的資源浪費。動態連結程式庫能較好的解決這個問題,重複利用代碼將大大提高了開發效率。一般,使用者在應用程式中基於如下要求建立和使用DLL:
1. 在不同的可執行檔之間共用的程式;
2. 在設計應用程式時,將其拆分成各個相互獨立功能組件,為以後這些功能組件各自升級提供方便的途徑。
動態連結程式庫將共用程式或功能組件做成庫中的函數,形成DLL檔案,其它應用程式通過使用DLL調用這些函數。在這裡將對Delphi和VC中建立和使用動態連結程式庫的方法做一介紹。Delphi和VC定義的動態連結程式庫均可在兩者的開發環境中使用。
一、 用Delphi建立DLL
Delphi的DLL建立並不複雜,下面向大家介紹Delphi的DLL建立方法。
(1)首先建立一個新的DLL項目(NewProject)project1,檔案頭部為:
(2)USES語句後面加入ExPorts語句,指明調用DLL的函數名,形式為:
exports checkpwd name 'checkpwd'; |
(3)在DLL的Pas檔案中Type......End後加入該DLL的函數或過程的聲明,形式如下:
| FunctionName(argment):Boolean;export; |
該函數也可以存在於另一個pas檔案中(如Unit1.pas),此時只需在專案檔中加入:
uses Unit1 in 'Unit1.pas' {Form1}; |
值得注意的是,該函數或過程應加入表單的Create和Free(產生和釋放)方法。
(4)對項目進行編譯即形成DLL檔案,可被其它項目調用。
二、Delphi中DLL的調用
調用DLL有兩種方法,一種是在應用程式裝載時調用,另一種是在應用程式運行時調用。
(1) 裝載時調用DLL
在調用DLL的Pas檔案中,對DLL函數進行外部聲明,聲明應位於Implementation後,形式如下:
Implementation Function functionname(argment):Boolean;far;External 'DllName'; |
其中External關鍵字後面的引號內是DLL的檔案名稱,該檔案一般應放在系統的system目錄下,或與調用它的項目同一目錄。聲明以後即可在Pas檔案任何地方引用DLL函數。
裝載時調用DLL的優點是速度較快,程式間也可共用代碼。
(2) 運行時調用DLL
DLL的另一種調用方法是在運行時調用。這種方法要調用到Windows的API函數LoadLibrary,GetProcAddress,FreeLibrary等。此方法主要用於調用其它語言,特別是C++編譯的DLL。
假定你欲調用的DLL中包括一個函數:
| Function checkpwd(pwd:string):boolean;export; |
那麼,首先在欲調用DLL的程式Type型別宣告處加入一句:
Type Tcheckpwd= function(pwd:string):boolean; |
此句的作用如同C++中聲明的函數指標。
然後定義如下變數∶
Var aptr:TFarproc; lhnd:THandle; flag:boolean; |
其中Aptr,lhnd兩變數聲明必須有,flag是DLL函數傳回值,視情況而定。在調用DLL處加入如下語句進行DLL裝載:
lhnd:=Loadlibrary('路徑:DLL檔案名稱');{如lhnd:=Loadlibrary('c:/project1.dll'); aptr:=GetprocAddress(lhnd,'checkpwd'); |
下面可直接調用DLL了:
| flag:=Tcheckpwd(aptr)( 'pwd');{根據函數填相應的變數參數} |
調用完以後,用FreeLibrary釋放DLL佔用的記憶體:
二、Delphi中DLL的調用
調用DLL有兩種方法,一種是在應用程式裝載時調用,另一種是在應用程式運行時調用。
(1) 裝載時調用DLL
在調用DLL的Pas檔案中,對DLL函數進行外部聲明,聲明應位於Implementation後,形式如下:
Implementation Function functionname(argment):Boolean;far;External 'DllName'; |
其中External關鍵字後面的引號內是DLL的檔案名稱,該檔案一般應放在系統的system目錄下,或與調用它的項目同一目錄。聲明以後即可在Pas檔案任何地方引用DLL函數。
裝載時調用DLL的優點是速度較快,程式間也可共用代碼。
(2) 運行時調用DLL
DLL的另一種調用方法是在運行時調用。這種方法要調用到Windows的API函數LoadLibrary,GetProcAddress,FreeLibrary等。此方法主要用於調用其它語言,特別是C++編譯的DLL。
假定你欲調用的DLL中包括一個函數:
| Function checkpwd(pwd:string):boolean;export; |
那麼,首先在欲調用DLL的程式Type型別宣告處加入一句:
Type Tcheckpwd= function(pwd:string):boolean; |
此句的作用如同C++中聲明的函數指標。
然後定義如下變數∶
Var aptr:TFarproc; lhnd:THandle; flag:boolean; |
其中Aptr,lhnd兩變數聲明必須有,flag是DLL函數傳回值,視情況而定。在調用DLL處加入如下語句進行DLL裝載:
lhnd:=Loadlibrary('路徑:DLL檔案名稱');{如lhnd:=Loadlibrary('c:/project1.dll'); aptr:=GetprocAddress(lhnd,'checkpwd'); |
下面可直接調用DLL了:
| flag:=Tcheckpwd(aptr)( 'pwd');{根據函數填相應的變數參數} |
調用完以後,用FreeLibrary釋放DLL佔用的記憶體:
三、用VC建立DLL
在VC中建立DLL,與Delphi中的建立過程相似,步驟如下:
(1)建立一個DLL項目。MFC提供兩種類型的動態連結程式庫:常規型DLL、擴充型DLL。其中,常規型DLL適合於非MFC開發環境下採用支援DLL調用的程式設計語言開發的應用程式,擴充型DLL只能被MFC程式使用。
(2)建立該DLL中的過程或函數,只需在程式中加入下列語句:
extern "C" _declspec(dllexport) int yanzheng(CString input,char* zcm) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); file://具體實現過程 } |
該函數的功能是驗證註冊碼是否合法,其中yanzheng為函數名,int為函數傳回值類型,()內為函數參數。編譯通過後即可使用。
四、 VC中DLL的調用
在VC中調用動態連結程式庫時,類似於Delphi中運行時調用DLL的方法。也要調用到Windows的API函數LoadLibrary,GetProcAddress等。此方法對於調用其它語言和VC編譯的DLL均適用。
假定DLL中包括一個函數:
| int yanzheng(CString input,char* zcm) |
首先在欲調用DLL函數的cpp檔案前部先聲明函數指標,定義函數類型:
| typedef int (* _YANZHENG)(CString,char* zcm); |
然後定義如下變數∶
HINSTANCE hLibrary; _YANZHENG yanzheng; |
在調用DLL處加入如下語句進行DLL裝載:
hLibrary=LoadLibrary("zcdll.dll"); yanzheng=(_YANZHENG)GetProcAddress(hLibrary,"yanzheng"); shuchu= (*yanzheng)(shuru,zcm); FreeLibrary(hLibrary); |
五、 幾點提示
(1)如果用Delphi建立DLL時用到其它的form,則在DLL的輸出函數中應包括form的create(建立)和free(釋放),而且在顯示該視窗時只能用showmodal過程,不能用show過程,在後面的例子中能詳細看到。
(2)如果Delphi建立的DLL中的函數或過程,用string類型作為參數輸入或返回string類型,當用VC或其它程式調用時,經常會出錯,反之亦然。為瞭解決這種情況,使不同程式之間在使用字串類型時,介面更安全,建議使用pchar類型,而不是string類型。而且要在程式中加入記憶體管理,使用系統提供的sharemem單元。下面是一個實現將星期標識由數字轉換成英文的例子:
uses sharemem; exports makeitaday; var mydate:pchar; function makeitaday(S:integer):pchar;stdcall;export; implementation function makeitaday(S:integer):pchar;stdcall;export; var thedate:array[0..20] of char; begin GetMem(mydate, 200); if s=1 then thedate:='Sunday '; file://………. strpcopy(mydate,thedate); makeitaday:=mydate; end; |
六、執行個體
因為用Delphi建立和調用DLL,過程與用VC建立和調用DLL相似,只是前者代碼要比在VC環境中稍微複雜一些,因此在文章的最後給出一個完整的用Delphi建立和調用DLL的例子。該DLL主要用來檢查輸入的口令是否正確,表單含有一個Edit編輯框,一個按鈕Button,在編輯框內輸入口令,比較編輯框的值和輸入參數返回真假值。
//檔案名稱為checkpassword.dpr;編譯此檔案產生checkpassword.dll library checkpassword; uses SysUtils, Classes, Unit1 in 'Unit1.pas' {Form1}; exports checkpwd name 'checkpwd';//聲明DLL函數 {$R *.RES} begin end.file://檔案名稱為unit1.pas unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) password: TEdit; Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; flag:boolean;//返回輸入密碼是否正確 rightpwd:string;//記錄輸入參數(正確的密碼) function checkpwd(pwd:string):boolean;export;//聲明DLL函數 implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin if password.text=rightpwd then flag:=true; form1.close; end; function checkpwd(pwd:string):boolean;export; begin flag:=false; rightpwd:=pwd;//讀入正確的密碼值 form1:=Tform1.create(Application);//建立密碼驗證視窗 form1.ShowModal;//顯示視窗 checkpwd:=flag; form1.free;//釋放資源 end; end. file://主程式main.pas,調用DLL unit main; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) jieguo: TEdit; Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation function checkpwd(pwd:string):boolean;external 'project1.dll' ;//函數說明 {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin if checkpwd('congrong') then//調用函數 jieguo.text:='true' else jieguo.text:='false'; end; end. |
以上程式在Windows98/Delphi5下編譯通過。