delphi調用DLL

調用一個DLL比寫一個DLL要容易一些。首先給大家介紹的是靜態調用方法,稍後將介紹動態調用方法,並就兩種方法做一個比較。同樣的,我們先舉一個靜態調用的例子。

unit Unit1; 

interface 

uses 
Windows, Messages, SysUtils, Classes, Graphics, 
Controls, Forms, Dialogs, StdCtrls; 

type 
TForm1 = class(TForm) 
Edit1: TEdit; 
Button1: TButton; 
procedure Button1Click(Sender: TObject); 
private 
{ Private declarations } 
public 
{ Public declarations } 
end; 

var 
Form1: TForm1; 

implementation 

{$R *.DFM} 


 

//本行以下代碼爲我們真正動手寫的代碼

function TestDll(i:integer):integer;stdcall; 
external ’Delphi.dll’; 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
Edit1.Text:=IntToStr(TestDll(1)); 
end; 

end. 

上面的例子中我們在窗體上放置了一個編輯框(Edit)和一個按鈕(Button),並且書寫了很少的代碼來測試我們剛剛編寫的Delphi.dll。大家可以看到我們唯一做的工作是將TestDll函數的說明部分放在了implementation中,並且用external語句指定了Delphi.dll的位置。(本例中調用程序和Delphi.dll在同一個目錄中。)讓人興奮的是,我們自己編寫的TestDll函數很快被Delphi認出來了。您可做這樣一個實驗:輸入“TestDll(”,很快Delphi就會用fly-by提示條提示您應該輸入的參數是什麼,就像我們使用Delphi中定義的其他函數一樣簡單。

注意事項有以下一些:

一、調用參數用stdcall。
和前面提到的一樣,當引用DLL中的函數和過程時也要使用stdcall參數,原因和前面提到的一樣。

二、用external語句指定被調用的DLL文件的路徑和名稱。
正如大家看到的,我們在external語句中指定了所要調用的DLL文件的名稱。沒有寫路徑是因爲該DLL文件和調用它的主程序在同一目錄下。如果該DLL文件在C:\,則我們可將上面的引用語句寫爲external ’C:\Delphi.dll’。注意文件的後綴.dll必須寫上。

三、不能從DLL中調用全局變量。
如果我們在DLL中聲明瞭某種全局變量,如:var s:byte 。這樣在DLL中s這個全局變量是可以正常使用的,但s不能被調用程序使用,既s不能作爲全局變量傳遞給調用程序。不過在調用程序中聲明的變量可以作爲參數傳遞給DLL。

四、被調用的DLL必須存在。
這一點很重要,使用靜態調用方法時要求所調用的DLL文件以及要調用的函數或過程等等必須存在。如果不存在或指定的路徑和文件名不正確的話,運行主程序時系統會提示“啓動程序時出錯”或“找不到*.dll文件”等運行錯誤。

編寫技巧
1 、爲了保證DLL的正確性,可先編寫成普通的應用程序的一部分,調試無誤後再從主程序中分離出來,編譯成DLL。

2 、爲了保證DLL的通用性,應該在自己編寫的DLL中杜絕出現可視化控件的名稱,如:Edit1.Text中的Edit1名稱;或者自定義非Windows定義的類型,如某種記錄。

3 、爲便於調試,每個函數和過程應該儘可能短小精悍,並配合具體詳細的註釋。

4 、應多利用try-finally來處理可能出現的錯誤和異常,注意這時要引用SysUtils單元。

5 、儘可能少引用單元以減小DLL的大小,特別是不要引用可視化單元,如Dialogs單元。例如一般情況下,我們可以不引用Classes單元,這樣可使編譯後的DLL減小大約16Kb。

調用技巧
1 、在用靜態方法時,可以給被調用的函數或過程更名。在前面提到的C++編寫的DLL例子中,如果去掉extern ”C”語句,C++會編譯出一些奇怪的函數名,原來的TestC函數會被命名爲@TestC$s等等可笑的怪名字,這是由於C++採用了C++ name mangling技術。這個函數名在Delphi中是非法的,我們可以這樣解決這個問題:
改寫引用函數爲
function TestC(i:integer):integer;stdcall;
external ’Cpp.dll’;name ’@TestC$s’;
其中name的作用就是重命名。

2 、可把我們編寫的DLL放到Windows目錄下或者Windows\system目錄下。這樣做可以在external語句中或LoadLibrary語句中不寫路徑而只寫DLL的名稱。但這樣做有些不妥,這兩個目錄下有大量重要的系統DLL,如果您編的DLL與它們重名的話其後果簡直不堪設想,況且您的編程技術還不至於達到將自己編寫的DLL放到系統目錄中的地步吧!

調試技巧
1 、我們知道DLL在編寫時是不能運行和單步調試的。有一個辦法可以,那就是在Run|parameters菜單中設置一個宿主程序。在Local頁的Host Application欄中添上宿主程序的名字就可進行單步調試、斷點觀察和運行了。

2 、添加DLL的版本信息。開場白中提到了版本信息對於DLL是很重要的,如果包含了版本信息,DLL的大小會增加2Kb。增加這麼一點空間是值得的。很不幸我們如果直接使用Project|options菜單中Version選項是不行的,這一點Delphi的幫助文件中沒有提到,經筆者研究發現,只要加一行代碼就可以了。如下例:

library Delphi; 

uses 
SysUtils, 
Classes; 

{$R *.RES} 
//注意,上面這行代碼必須加在這個位置 

function TestDll(i:integer):integer;stdcall; 
begin 
Result:=i; 
end; 

exports 
TestDll; 

begin 
end. 

3 、爲了避免與別的DLL重名,在給自己編寫的DLL起名字的時候最好採用字符數字和下劃線混合的方式。如:jl_try16.dll。

4 、如果您原來在Delphi 1或Delphi 2中已經編譯了某些DLL的話,您原來編譯的DLL是16位的。只要將源代碼在新的Delphi 3或Delphi 4環境下重新編譯,就可以得到32位的DLL了

參考文章:在Delphi與C++之間實現函數與對象共享 http://www.zahui.com/html/2/4202.htm

1.C++共享Delphi對象

要實現從C++調用Delphi對象,首先要在Delphi單元的接口部分以及C++的頭文件中說明需要共享的對象的接口,在對象接口中定義該對象包含哪些屬性與方法,並說明可供共享的部分。對象的共享,關鍵在於方法的共享。在Delphi語言中,要使一個對象可以被共享,可以把它說明爲兩個接口部分,暫稱爲"共享接口"與"實現接口"。其中共享接口指明對象中哪些方法可被另一種語言所共享;實現接口則繼承共享接口,並且在單元實現部分針對實現接口中的方法定義具體的實現。要定義一個可供C++共享的Delphi對象,共享接口的說明應注意:

在Delphi程序裏,要共享的方法必須被說明爲抽象(abstract),而且虛擬(virtual );

在C++程序裏,必須用關鍵字"virtual"及"=0"後綴,把從Delphi共享的方法說明成"pure virtual";

共享的對象方法必須在兩種語言裏都被說明成相同的調用方式,通常使用標準系統調用方式(stdcall)。

下面,舉例說明這些規則,假設有這樣的一個Delphi對象:
TTestObject=class
procedure Proc1(x:integer);
function Func1(x:integer):PChar;
procedure Proc2;
function Func2:integer;
end;

如果C++程序需要共享其中的方法Proc1、Func1,可把上述說明修改成以下形式:
STestObject=class
procedure Proc1(x:integer); virtual; abstract; stdcall;
function Func1(x:integer); virtual; abstract; stdcall;
end;
TTestObject=class(STestObject)
procedure Proc1(x:integer);
fuction Func1(x:integer):PChar;
procedure Proc2;
fuction Func2:integer;
end;

在C++程序中做如下對象原型說明:
class STestObject {
virtual void Proc1(int x)=0;
virtual char *Func1(int x)=0;
};

爲了能在C++中成功地訪問Delphi定義的類, Delphi接口說明時必須包含一個可共享的"製造函數(Factory Function)"CreateTestObject,該製造函數可被定義在動態鏈接庫或目標文件(.OBJ)中,例如:

Library TestLib;
exports CreateTestObject;
function CreateTestObject:STestObject; stdcall;
begin
Result:=TTestObject.Create;
end;

end.

經過這樣的處理,現在可在C++程序中使用這個由Delphi定義的對象,調用方式如下:
extern "C" STestObject stdcall *CreateTestObject();
void UseTestObject(void) {
STestObject *theTestObject=CreateTestObject();
theTestObject->Proc1(10);
Char *str=theTestObject->Func1(0);
}

當調用製造函數CreateTestObject時,實際上已經在Delphi一側佔用了一個對象實例的空間,C++程序在針對該對象的所有處理完成後必須考慮釋放這一空間,具體的實現可在Delphi中定義一個類,如上述Proc1的共享方法Free,以此來完成這一任務:
STestObject=class
procedure Proc1(x:integer); virtual; abstract; stdcall;
function Func1(x:integer); virtual; abstract; stdcall;
procedure Free; virtual; abstract; stdcall;
end;

implementation

procedure TTestObject.Free;
begin

end;

end.

2.Delphi共享C++對象
通常,程序員會考慮使用Delphi來編制用戶界面,所以Delphi代碼調用C++代碼似乎顯得更加實際些。其實,Delphi共享C++對象的實現方法與上述C++共享Delphi對象非常相似。用同樣的共享接口與實現接口說明方法來定義C++的類:

class STestObjedt {
virtual void Proc1(int x)=0;
virtual char *Func1(int x)=0;
};
class TTestObjedt :public STestObject {
void Proc1(int x);
char *Func1(int x);
void Proc2();
int Func2();
void Free();
};

然後實現這些方法。同樣地,C++對象需要一個與之對應的製造函數,這裏以DLL爲例
STestObject stdcall export *CreateTestObject() {
return (STestObject *) new TTestObject.Create;
}

Delphi代碼可以通過調用製造函數CreateTestObject,很容易地在C++中創建實例,獲得指向該實例的指針值,並以這個指針值來調用對象中的共享方法。當然,在進行完該對象的相關處理後,千萬不要忘了調用Free釋放佔用的空間。
張維

摘要:Delphi以其獨特的面向控件的開發方式、強大的數據庫功能以及快速的編譯技術,使得它自發布起即格外引人注意。隨着Delphi 5提供豐富的Internet應用,Delphi日益成爲最重要的軟件開發工具之一,它吸引了許多原Visual Basic、Foxpro、dBase甚至C++的程序員,而這些程序員使用Delphi時需要解決的一個重要問題就是怎樣利用他們原有的代碼。本文將介紹Delphi與C++程序集成的方法,包括:
Delphi與C++之間函數的共享;
代碼的靜態鏈接和動態鏈接;
對象的共享。
函數的共享

在Delphi中調用C++函數與C++調用Delphi函數相當直接,需要注意的是,Delphi 1默認的函數調用方式是Pascal方式,Delphi 4、Delphi 5的默認方式則是優化的cdecl調用方式,即register方式。要在C++與Delphi程序之間實現函數共享,除非有充分的原因,否則應該使用標準系統調用方式,即stdcall方式。爲了使C++編譯器不將函數標記爲"mang led",使Delphi編譯器誤認爲函數是採用cdecl調用方式,應該在C++代碼中,以extern "C "說明被共享的函數,如下例所示:

原型說明:
在C++中:
extern "C" int _stdcall TestFunc();
在Delphi中:
function TestFunc:integer; stdcall;

調用語法:
在C++中:
int i=TestFunc();
在Delphi中:
var i:integer;

begin

i:=TestFunc;

end;

共享函數的參數必須是兩種語言都支持的變量類

參考文章:DELPHI 中動態鏈接庫的使用 http://blog.csdn.net/feifei1018/admin/EditPosts.aspx

在DELPHI中,有兩種方法可用於調用一個儲存在DLL(動態鏈接庫)中的過程。

一、 調用方法

1、 靜態調用或顯式裝載使用一個外部聲明子句,使DLL在應用程序開始執行前即被裝入。例如:

Function instring (sourcestr: Pchar ;
check: char): integer; far; external ‘ demostr’
這種方式要在單元的interface 部分用external 指示字列出要從DLL中調用的例程。Far 指令表明可以被其他段,例如其他單元調用的子例程。所有在單元接口中聲明的子例程在缺省情況下都是Far類型的,其相反的指令是near。

如果external 後什麼也不跟,必須用 {$ L } 編譯指令預先指定一個DLL名字,如:

{ $ L Mydlls.dll }
Procedure setstring(var str: string) ;
stdcall ; external
但是使用靜態調用方法時,程序無法在運行時間裏決定DLL的調用。在DELPHI中使用DLL時,例程的標識符必須與DLL中相應輸出例程的標識符完全一致(儘管DELPHI本身大小寫不敏感)。

2、 動態調用或隱式裝入

使用WINDOWS API 函數 Loadlibrary 和GetprocAddress可以實現在運行時間裏的動態裝載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


 

二、 調用方式

1、 通過過程、函數名;

2、 通過過程、函數別名;

3、 通過過程、函數的順序號

例:Function Getstring : string ; stdcall ; external ‘Mydlls.dll’ name ‘Mygetstr’name 子句指定函數名Getstring 改爲Mygetstr,當程序調用這個例程時,使用Mygetstr這個名字;Function Getstring : string ; stdcall ; external ‘Mydlls.dll’ index 5 Index 子句通過索引號引入例程可以減少DLL的加載時間。

三、 調用約定

調用約定,是指調用例程時參數的傳遞順序。DELPHI中DLL支持的調用約定有:

調用約定 參數傳遞順序
Register 從左到右
Pascal 從左到右
Stdcall 從右到左
Cdecl 從右到左
Safecall 從右到左
使用Stdcall 方式,能保證不同語言寫的DLL的兼容性,同時它也是WINDOWS API的約定方式;Delphi 3。0、4。0的默認調用方式爲Register ;Cdecl是採用 C/C++的調用約定,適用於DLL是由C++語言編寫的;Safecall 是適合於聲明OLE對象中的方法。

四、 DLL中的變量和段

一個DLL聲明的任何變量都爲自己私有 ,調用它的模塊不能直接使用它定義的變量。要使用時必須通過過程或函數界面才能完成,對DLL來說,它永遠都沒有機會使用調用它的模塊中的聲明的變量。一個DLL沒有自己的SS(堆棧段),它使用調用它的應用程序的堆棧。因此在DLL中的過程、函數不要假定DS=SS(DS爲數據段)。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章