http://www.cnblogs.com/IceKernel/articles/2848848.htmlhtml
http://blog.csdn.net/welcome000yy/article/details/7845319程序員
在DELPHI中,有兩種方法調用一個儲存在DLL(動態連接庫)中的函數和過程,即靜態調用或者動態調用。app
1)、 靜態調用或顯式裝載函數
使用一個外部聲明子句,使DLL在應用程序開始執行前即被裝入。例如:學習
Function instring (sourcestr: Pchar ; check: char): integer; far; external 'demostr'
這種方式要在單元的interface 部分用external 指示字列出要從DLL中調用的例程。Far 指令代表能夠被其餘段(例如其餘單元)調用的子例程。全部在單元接口中聲明的子例程在缺省狀況下都是Far類型的,其相反的指令是near。spa
若是external 後什麼也不跟,必須用 {$ L } 編譯指令預先指定一個DLL名字,如:.net
{ $ L Mydlls.dll } Procedure setstring(var str: string) ; stdcall ; external
可是使用靜態調用方法時,程序沒法在運行時間裏決定DLL的調用。在DELPHI中使用DLL時,例程的標識符必須與DLL中相應輸出例程的標識符徹底一致(儘管DELPHI自己大小寫不敏感)。使用外部聲明的缺點是程序啓動時若是找不到mydll.dll將沒法運行,即便沒有調用其中的模塊。 動態加載的方法能夠避免這種狀況。 3d
2)、 動態調用或隱式裝入指針
使用WINDOWS API 函數 Loadlibrary 和GetprocAddress能夠實如今運行時間裏的動態裝載DLL,並調用其中的過程。將DLL調入內存並得到指向函數或過程的指針,執行完模塊後釋放內存。除了節約內存外,這種方法的一個很大的優勢是能處理找不到dll或者在裝入過程當中出錯的狀況。這樣即便某個dll有問題,應用程序的其餘部分仍然可以正常運行。動態加載的例子以下:
Type TMyProc=Procedure (Param:Pchar ) ;Stdcall; Var MyProc: TMyproc; MyHandle:THandle; MyHandle:=LoadLibrary ('Mydll') ; If MyHandle<=0 then Raise Exception.Create( '動態連接庫調用失敗,錯誤代碼是:'+Inttostr(Getlasterror)) else @MyProc:=GetProcAddress(MyHandle,'demoproc'); if not Assigned(MyProc) then Raise Exception.Create('GetProcAddress 調用失敗,錯誤代碼是:'+inttostr(getlasterror)) else MyProc(Pchar('a string')); Freelibrary(Myhandle); // 卸載DLL
3)、 調用方式
經過過程、函數名;
經過過程、函數別名;
經過過程、函數的順序號
舉例以下:在MYDLL.DLL中有兩個函數和一個過程,則其外部聲明能夠寫成:
例:
Function Getstring : string ; stdcall ; external 'Mydlls.dll' name 'Mygetstr' //name 子句指定函數名Getstring 改成Mygetstr,當程序調用這個例程時,使用Mygetstr這個名字; Function Getstring : string ; stdcall ; external 'Mydlls.dll' index 5 //Index 子句經過索引號引入例程能夠減小DLL的加載時間。
4)、 調用約定
調用約定,是指調用例程時參數的傳遞順序。DELPHI中DLL支持的調用約定有:
調用約定 | 參數傳遞順序 |
Register | 從左到右 |
Pascal | 從左到右 |
Stdcall | 從右到左 |
Cdecl | 從右到左 |
Safecall | 從右到左 |
使用Stdcall 方式,能保證不一樣語言寫的DLL的兼容性,同時它也是WINDOWS API的約定方式; Delphi 3.0、4.0的默認調用方式爲Register; Cdecl是採用 C/C++的調用約定,適用於DLL是由C++語言編寫的; Safecall 是適合於聲明OLE對象中的方法。
5)、 DLL中的變量和段
一個DLL聲明的任何變量都爲本身私有 ,調用它的模塊不能直接使用它定義的變量。要使用時必須經過過程或函數界面才能完成,對DLL來講,它永遠都沒有機會使用調用它的模塊中的聲明的變量。一個DLL沒有本身的SS(堆棧段),它使用調用它的應用程序的堆棧。所以在DLL中的過程、函數不要假定DS=SS(DS爲數據段)。
用Delphi建立一個DLL是十分簡單的,首先須要新建一個DLL的Porject:選擇File/New/Others...,以下:
選擇「Dll Wizard」,點擊 OK 按鈕。
內容實例以下:
library DemoSvr; { Important note about DLL memory management: ShareMem must be the first unit in your library's USES clause AND your project's (select Project-View Source) USES clause if your DLL exports any procedures or functions that pass strings as parameters or function results. This applies to all strings passed to and from your DLL--even those that are nested in records and classes. ShareMem is the interface unit to the BORLNDMM.DLL shared memory manager, which must be deployed along with your DLL. To avoid using BORLNDMM.DLL, pass string information using PChar or ShortString parameters. } uses SysUtils,Classes; function Test1(a,b:integer):integer; begin Result:=a+b; end; exports Test1 index 1; begin end.
在這個DLL裏咱們聲明瞭一個加法函數,而後用exports語句輸出它,只有被輸出的函數或過程能被其餘程序調用。
調試(編譯/Ctrl+F9)成功之後 會生成一個DemoSvr.dll 文件。能夠設置編譯的路徑,選擇"Project/Options..."菜單:
在DLL項目中,能夠指定一個宿主程序來調試,具體方法爲選擇 "Run/Parameters..."菜單:
另外,能夠設置跟蹤調試:
exports語句的語法是:
函數名 [index <n>],
index <n>是爲函數手工指定索引號,以便其餘程序肯定函數地址;也能夠不指定,若是沒有使用Index關鍵字,Delphi將按照exports後的順序從1開始自動分配索引號。如今咱們能夠調用這個DLL了,下面給出一個實例,運行後form1的標題將變成「1+2=3」:
聲明部分:function Test1(a,b:integer):integer;external 'DemoSvr'; //注意此處是大小寫敏感的。 運行部分:form1.caption:='1+2='+inttostr(test1(1,2));
(1)把現有的項目改爲DLL
學會製做DLL之前,大多數程序員手中都積攢下來很多已經完成了的項目,若是如今須要把這些項目作成DLL而不是可執行文件,從新寫一遍顯然是沒有必要的,只要按照下面的步驟對已有的項目文件進行修改就能夠了:
① 打開項目文件(.DPR),刪除單元底部begin和end.之間的全部語句(通常狀況下這些語句是由Delphi自動生成的)。若是項目中沒有用到Form,則從uses子句中刪除表單單元(Form),而後轉到第③步。
② 對項目進行修改,令除Main Form以外的全部Form都是動態生成的,這樣咱們只要在DLL輸出的一個函數或者過程當中生成Main Form,便可調用執行整個項目。咱們假設Main Form的名字是MyMainForm,項目的名字是MyDll,如今在單元底部的begin語句以前加入一個過程,過程的名字爲RunMyDll,這個過程將動態生成Main Form,從而運行整個項目。RunMyDll的寫法以下:
procedure InitDll2; begin Application.CreateForm(TMyMainForm, MyMainForm); MyMainForm.Show; //若是MyMainForm不可視則須要這一句. end;
③ 若是想要輸出其餘函數或者過程,而原來的項目中沒有,則能夠在單元底部的begin語句以前加入這些代碼。
④ 在單元底部的begin語句以前加入一個exports小節,而後寫出全部想要輸出的函數或過程的名字(最好指定索引號)。注意若是執行了第②步,必定要輸出RunMyDll過程。
⑤ 將項目文件頂部的保留字program改成library。
⑥ 編譯。
如今就能夠在其餘程序中調用本項目中的函數和過程了,只要執行RunMyDll就能夠執行這個項目,和執行原來的可執行文件如出一轍。
(2)建立一個引入文件
若是DLL比較複雜,則爲它的聲明專門建立一個引入程序單元將是十分有意義的,而且會使這個DLL變得更加容易維護。引入單元的格式以下:
unit MyImport; {Import unit for MyDll.Dll} interface procedure RunMyDll; implementation procedure RunMyDll;external 'MyDll' index 1; end.
這樣之後想要使用MyDll中的例程時,只要簡單的在程序模塊中的uses子句中加上MyImport便可。