如何用Delphi編寫(xiě)dll文件
一、開(kāi)使你的第一個(gè)DLL專(zhuān)案
1.File->Close all->File->New﹝DLL﹞
代碼:
//自動(dòng)產(chǎn)生Code如下
library Project2;
//這有段廢話(huà)
uses
SysUtils,
Classes;
{$R *.RES}
begin
end.
2.加個(gè)Func進(jìn)來(lái):
代碼:
library Project2;
uses
SysUtils,
Classes;
Function MyMax ( X , Y : integer ) : integer ; stdcall ;
begin
if X > Y then
Result := X
else
Result := Y ;
end ;
//切記:Library 的名字大小寫(xiě)沒(méi)關(guān)系,可是DLL-Func的大小寫(xiě)就有關(guān)系了。
// 在 DLL-Func-Name寫(xiě)成MyMax與myMAX是不同的。如果寫(xiě)錯(cuò)了,立即
// 的結(jié)果是你叫用到此DLL的AP根本開(kāi)不起來(lái)。
//參數(shù)的大小寫(xiě)就沒(méi)關(guān)系了。甚至不必同名。如原型中是 (X,Y:integer)但引
// 用時(shí)寫(xiě)成(A,B:integer),那是沒(méi)關(guān)系的。
//切記:要再加個(gè)stdcall。書(shū)上講,如果你是用delphi寫(xiě)DLL,且希望不僅給
// delphi-AP也希望BCB/VC-AP等使用的話(huà),那你最好加個(gè)Stdcall ; 的指示
//參數(shù)型態(tài):delphi有很多種它自己的變量型態(tài),這些當(dāng)然不是DLL所喜歡的
// ,Windows/DLL的母語(yǔ)應(yīng)該是C。所以如果要傳進(jìn)傳出DLL的參數(shù),我們
// 盡可能照規(guī)矩來(lái)用。這兩者寫(xiě)起來(lái),后者會(huì)麻煩不少。如果你對(duì)C不熟
// 的話(huà),那也沒(méi)關(guān)系。我們以后再講。
{$R *.RES}
begin
end.
3.將這些可共享的Func送出DLL,讓外界﹝就是你的delphi-AP啦﹞使用:光如此,你的AP還不能用到這些,你還要加個(gè)Exports才行。
代碼:
{$R *.RES}
exports
MyMax ;
begin
end.
4.好了,可以按 Ctrl-F9編譯了。此時(shí)可不要按F9。DLL不是EXE┌不可單獨(dú)執(zhí)行的,如果你按F9,會(huì)有ErrorMsg的。這時(shí)如果DLL有Error,請(qǐng)修正之。再按Ctrl-F9。此時(shí)可能有Warning,不要緊,研究一下,看看就好。再按Ctrl-F9,此時(shí)就『Done , Compiled 』。同目錄就會(huì)有個(gè) *.dll 。恭喜,大功告成了。
二、進(jìn)行測(cè)試:開(kāi)個(gè)新application:
1.加個(gè)TButton
代碼:
ShowMessage ( IntToStr(MyMax(30,50)) ) ;
2.告知Exe到那里抓個(gè)Func
代碼:
//在Form,interface,var后加
Function MyMax ( X , Y : integer ) : integer ; stdcall ; external 'MyTestDLL.dll' ;
// MyTestDLL.dll為你前時(shí)寫(xiě)的DLL項(xiàng)目名字
// DLL名字大小寫(xiě)沒(méi)關(guān)系。不過(guò)記得要加 extension的 .DLL。在Win95或NT,
// 是不必加 extension,但這兩種OS,可能越來(lái)越少了吧。要加extension
可以了,簡(jiǎn)單吧。
上面的例子是不是很簡(jiǎn)單?熟悉delphi的朋友可以看出以上代碼和一般的delphi程序的編寫(xiě)基本是相同的,只是在TestDll函數(shù)后多了一個(gè)stdcall參數(shù)并且用exports語(yǔ)句聲明了TestDll函數(shù)。只要編譯上面的代碼,就可以玫揭桓雒 狣elphi.dll的動(dòng)態(tài)鏈接庫(kù)?,F(xiàn)在,讓我們來(lái)看看有哪些需要注意的地方:
1.在DLL中編寫(xiě)的函數(shù)或過(guò)程都必須加上stdcall調(diào)用參數(shù)。在delphi 1或delphi 2環(huán)境下該調(diào)用參數(shù)是far。從delphi 3以后將這個(gè)參數(shù)變?yōu)榱藄tdcall,目的是為了使用標(biāo)準(zhǔn)的Win32參數(shù)傳遞技術(shù)來(lái)代替優(yōu)化的register參數(shù)。忘記使用stdcall參數(shù)是常見(jiàn)的錯(cuò)誤,這個(gè)錯(cuò)誤不會(huì)影響DLL的編譯和生成,但當(dāng)調(diào)用這個(gè)DLL時(shí)會(huì)發(fā)生很?chē)?yán)重的錯(cuò)誤,導(dǎo)致操作系統(tǒng)的死鎖。原因是register參數(shù)是delphi的默認(rèn)參數(shù)。
2.所寫(xiě)的函數(shù)和過(guò)程應(yīng)該用exports語(yǔ)句聲明為外部函數(shù)。
正如大家看到的,TestDll函數(shù)被聲明為一個(gè)外部函數(shù)。這樣做可以使該函數(shù)在外部就能看到,具體方法是單激鼠標(biāo)右鍵用“快速查看(Quick View)”功能查看該DLL文件。(如果沒(méi)有“快速查看”選項(xiàng)可以從Windows CD上安裝。)TestDll函數(shù)會(huì)出現(xiàn)在Export Table欄中。另一個(gè)很充分的理由是,如果不這樣聲明,我們編寫(xiě)的函數(shù)將不能被調(diào)用,這是大家都不愿看到的。
3.當(dāng)使用了長(zhǎng)字符串類(lèi)型的參數(shù)、變量時(shí)要引用ShareMem。
delphi中的string類(lèi)型很強(qiáng)大,我們知道普通的字符串長(zhǎng)度最大為256個(gè)字符,但delphi中string類(lèi)型在默認(rèn)情況下長(zhǎng)度可以達(dá)到2G。(對(duì),您沒(méi)有看錯(cuò),確實(shí)是兩兆。)這時(shí),如果您堅(jiān)持要使用string類(lèi)型的參數(shù)、變量甚至是記錄信息時(shí),就要引用ShareMem單元,而且必須是第一個(gè)引用的。既在uses語(yǔ)句后是第一個(gè)引用的單元。如下例:
uses
ShareMem,
SysUtils,
Classes;
還有一點(diǎn),在您的工程文件(*.dpr)中而不是單元文件(*.pas)中也要做同樣的工作,這一點(diǎn)delphi自帶的幫助文件沒(méi)有說(shuō)清楚,造成了很多誤會(huì)。不這樣做的話(huà),您很有可能付出死機(jī)的代價(jià)。避免使用string類(lèi)型的方法是將string類(lèi)型的參數(shù)、變量等聲明為Pchar或ShortString(如:s:string[10])類(lèi)型。同樣的問(wèn)題會(huì)出現(xiàn)在當(dāng)您使用了動(dòng)態(tài)數(shù)組時(shí),解決的方法同上所述。
Delphi DLL動(dòng)態(tài)鏈庫(kù)編寫(xiě)教程
Windows的發(fā)展要求允許同時(shí)運(yùn)行的幾個(gè)程序共享一組函數(shù)的單一拷貝。動(dòng)態(tài)鏈接庫(kù)就是在這種情況下出現(xiàn)的。動(dòng)態(tài)鏈接庫(kù)不用重復(fù)編譯或鏈接,一旦裝入內(nèi)存,Dlls函數(shù)可以被系統(tǒng)中的任何正在運(yùn)行的應(yīng)用程序軟件所使用,而不必再將DLLs函數(shù)的另一拷貝裝入內(nèi)存?!∪魏螒?yīng)用程序都可以共享由裝入內(nèi)存的DLLs管理的內(nèi)存資源塊。只包含共享數(shù)據(jù)的DLLs稱(chēng)為資源文件。
1.一般工程文件的頭標(biāo)用program關(guān)鍵字,而DLLs工程文件頭標(biāo)用library 關(guān)鍵字。不同的關(guān)鍵字通知編譯器生成不同的可執(zhí)行文件。用program關(guān)鍵字生成的是.exe文件,而用library關(guān)鍵字生成的是.dll文件;
2.假如DLLs要輸出供其它應(yīng)用程序使用的函數(shù)或過(guò)程,則必須將這些函數(shù)或過(guò)程列在exports子句中。而這些函數(shù)或過(guò)程本身必須用export編譯指令進(jìn)行編譯。
根據(jù)DLLs完成的功能,我們把DLLs分為如下的三類(lèi):
1.完成一般功能的DLLs;2.用于數(shù)據(jù)交換的DLLs;3.用于窗體重用的DLLs。
一 Dll的制作一般分為以下幾步:
1 在一個(gè)DLL工程里寫(xiě)一個(gè)過(guò)程或函數(shù)
2 寫(xiě)一個(gè)Exports關(guān)鍵字,在其下寫(xiě)過(guò)程的名稱(chēng)。不用寫(xiě)參數(shù)和調(diào)用后綴。
二 參數(shù)傳遞
1 參數(shù)類(lèi)型最好與window C++的參數(shù)類(lèi)型一致。最好少用DELPHI的數(shù)據(jù)類(lèi)型。
2 最好有返回值[即使是一個(gè)過(guò)程],來(lái)報(bào)出調(diào)用成功或失敗,或狀態(tài)。成功或失敗的返回值最好為1[成功]或0[失敗].一句話(huà),與windows c++兼容。
3 用stdcall聲明后綴。
4 最好大小寫(xiě)敏感。
5 無(wú)須用far調(diào)用后綴,那只是為了與windows 16位程序兼容。
三 DLL的初始化和退出清理[如果需要初始化和退出清理]
1 DLLProc[SysUtils單元的一個(gè)Pointer]是DLL的入口。在此你可用你的函數(shù)替換了它的入口。但你的函數(shù)必須符合以下要求[其實(shí)就是一個(gè)回調(diào)函數(shù)]。如下:
procedurefar;stdcall;
dwReason參數(shù)有四種類(lèi)型:
DLL_PROCESS_ATTACH:進(jìn)程進(jìn)入時(shí) ??????DLL_PROCESS_DETACH進(jìn)程退出時(shí)
DLL_THREAD_ATTACH 線(xiàn)程進(jìn)入時(shí) ???????DLL_THREAD_DETACH 線(xiàn)程退出時(shí)
在初始化部分寫(xiě):
DLLProc := @DLLEnterPoint;
DllEnterPoint(DLL_PROCESS_ATTACH);
2 如Form上有TdcomConnection組件,就Uses Activex,在初始化時(shí)寫(xiě)一句CoInitialize (nil);
3 在退出時(shí)一定保證DcomConnection.Connected := False,并且數(shù)據(jù)集已關(guān)閉。否則報(bào)地址錯(cuò)。
四 全局變量的使用
在widnows 32位程序中,兩個(gè)應(yīng)用程序的地址空間是相互沒(méi)有聯(lián)系的。雖然DLL在內(nèi)存中是一份,但變量是在各進(jìn)程的地址空間中,因此你不能借助dll的全局變量來(lái)達(dá)到兩個(gè)應(yīng)用程序間的數(shù)據(jù)傳遞,除非你用內(nèi)存映像文件。
五 調(diào)用靜態(tài)載入
1 客戶(hù)端函數(shù)聲名:
1)大小寫(xiě)敏感。
2)與DLL中的聲明一樣。如:Far;external'yproject_dll.dll';
3)調(diào)用時(shí)傳過(guò)去的參數(shù)類(lèi)型最好也與windows c++一樣。
4)調(diào)用時(shí)DLL必須在windows搜索路徑中,順序是:當(dāng)前目錄;Path路徑;windows;widows\system;windows\ssystem32;
六 調(diào)用動(dòng)態(tài)載入
1 建立一種過(guò)程類(lèi)型[如果你對(duì)過(guò)程類(lèi)型的變量只是一個(gè)指針的本質(zhì)清楚的話(huà),你就知道是怎么回事了]。如:
type
mypointer=procedure(form:Tform);Far;external;
var
Hinst:Thandle;
begin
Hinst:=loadlibrary('yproject_dll');//Load一個(gè)Dll,按文件名找。
showform:=getprocaddress(Hinst,'showform');//按函數(shù)名找,大小寫(xiě)敏感。如果你知道自動(dòng)化對(duì)象的本質(zhì)就清楚了。
showform(application.mainform);//找到函數(shù)入口指針就調(diào)用。
Freelibrary(Hinst);
end;
例:
1.File->Close all->File->New﹝DLL﹞
代碼: //自動(dòng)產(chǎn)生Code如下
library Project2;
????uses
????SysUtils, Classes;
????{$R *.RES}
????begin
????end.
2.加個(gè)Func進(jìn)來(lái):
????代碼:
????library Project2;
????uses
????SysUtils, Classes;
Function stdcall ;
begin
if X > Y then ??????Result := X
else ???????????????Result := Y ;
end ;
//切記:Library 的名字大小寫(xiě)沒(méi)關(guān)系,可是DLL-Func的大小寫(xiě)就有關(guān)系了。??在 DLL-Func-Name寫(xiě)成MyMax與myMAX是不同的。如果寫(xiě)錯(cuò)了,結(jié)果是此DLL的AP根本打不開(kāi)。參數(shù)的大小寫(xiě)就沒(méi)關(guān)系了。甚至不必同名。如原型中是 (X,Y:integer)但引用時(shí)寫(xiě)成(A,B:integer),那是沒(méi)關(guān)系的。
//切記:要再加個(gè)stdcall。書(shū)上講,如果你是用delphi寫(xiě)DLL,且希望不僅給delphi-AP也希望BCB/VC-AP等使用的話(huà),那你最好加個(gè)Stdcall ; 的指示
//參數(shù)型態(tài):delphi有很多種它自己的變量型態(tài),這些當(dāng)然不是DLL所喜歡的,Windows/DLL的母語(yǔ)是C。所以如果要傳進(jìn)傳出DLL的參數(shù),我們盡可能照規(guī)矩來(lái)用。
{$R *.RES}
begin
end.
3.將這些可共享的Func送出DLL,讓外界﹝就是你的delphi-AP啦﹞使用:光如此,你的AP還不能用到這些,你還要加個(gè)Exports才行。
代碼:
{$R *.RES}
exports
MyMax ;
begin
end.
4.好了,可以按 Ctrl-F9編譯了。此時(shí)可不要按F9。DLL不是EXE不可單獨(dú)執(zhí)行的,如果你按F9,會(huì)有ErrorMsg的。這時(shí)如果DLL有Error,請(qǐng)修正之。再按Ctrl-F9。此時(shí)可能有Warning,不要緊,研究一下,看看就好。再按Ctrl-F9,此時(shí)就『Done , Compiled 』。同目錄就會(huì)有個(gè) *.dll 。恭喜,大功告成了。
二、進(jìn)行測(cè)試:開(kāi)個(gè)新application:
1.加個(gè)TButton
????????代碼: ShowMessage ( IntToStr(MyMax(30,50)) ) ;
2.告知Exe到那里抓個(gè)Func
????????代碼: //在Form,interface,var后加
Function stdcall ; external 'MyTestDLL.dll' ;
// MyTestDLL.dll為你前時(shí)寫(xiě)的DLL項(xiàng)目名字
// DLL名字大小寫(xiě)沒(méi)關(guān)系。不過(guò)記得要加 extension的 .DLL。在Win95或NT,是不必加 extension,但這兩種OS,可能越來(lái)越少了吧。要加extension
在delphi 1或delphi 2環(huán)境下該調(diào)用參數(shù)是far。從delphi 3以后將這個(gè)參數(shù)變?yōu)榱藄tdcall,目的是為了使用標(biāo)準(zhǔn)的Win32參數(shù)傳遞技術(shù)來(lái)代替優(yōu)化的register參數(shù)。
1.在DLL中編寫(xiě)的函數(shù)或過(guò)程都必須加上stdcall調(diào)用參數(shù)。
2.所寫(xiě)的函數(shù)和過(guò)程應(yīng)該用exports語(yǔ)句聲明為外部函數(shù)。
3.當(dāng)使用了長(zhǎng)字符串類(lèi)型的參數(shù)、變量時(shí)要引用ShareMem。
一、正如大家看到的,我們?cè)趀xternal語(yǔ)句中指定了所要調(diào)用的DLL文件的名稱(chēng)。沒(méi)有寫(xiě)路徑是因?yàn)樵揇LL文件和調(diào)用它的主程序在同一目錄下。如果該DLL文件在C:\,則我們可將上面的引用語(yǔ)句寫(xiě)為external ’C:\delphi.dll’。注意文件的后綴.dll必須寫(xiě)上。
二、不能從DLL中調(diào)用全局變量
如果我們?cè)贒LL中聲明了某種全局變量,如:var s:byte 。這樣在DLL中s這個(gè)全局變量是可以正常使用的,但s不能被調(diào)用程序使用,既s不能作為全局變量傳遞給調(diào)用程序。不過(guò)在調(diào)用程序中聲明的變量可以作為參數(shù)傳遞給DLL。
三、被調(diào)用的DLL必須存在
這一點(diǎn)很重要,使用靜態(tài)調(diào)用方法時(shí)要求所調(diào)用的DLL文件以及要調(diào)用的函數(shù)或過(guò)程等等必須存在。如果不存在或指定的路徑和文件名不正確的話(huà),運(yùn)行主程序時(shí)系統(tǒng)會(huì)提示“啟動(dòng)程序時(shí)出錯(cuò)”或“找不到*.dll文件”等運(yùn)行錯(cuò)誤。
1 、我們知道DLL在編寫(xiě)時(shí)是不能運(yùn)行和單步調(diào)試的。有一個(gè)辦法可以,那就是在Run|parameters菜單中設(shè)置一個(gè)宿主程序。在Local頁(yè)的Host Application欄中添上宿主程序的名字就可進(jìn)行單步調(diào)試、斷點(diǎn)觀(guān)察和運(yùn)行了。
2 、添加DLL的版本信息。開(kāi)場(chǎng)白中提到了版本信息對(duì)于DLL是很重要的,如果包含了版本信息,DLL的大小會(huì)增加2Kb。增加這么一點(diǎn)空間是值得的。很不幸我們?nèi)绻苯邮褂肞roject|options菜單中Version選項(xiàng)是不行的,這一點(diǎn)delphi的幫助文件中沒(méi)有提到,經(jīng)筆者研究發(fā)現(xiàn),只要加一行代碼就可以了。如下例:
library delphi;
uses
SysUtils, Classes;
{$R *.RES}
//注意,上面這行代碼必須加在這個(gè)位置
functionstdcall;
begin ???Result:=i; ???end;
exports TestDll;
begin
end.
1 、在用靜態(tài)方法時(shí),可以給被調(diào)用的函數(shù)或過(guò)程更名。在前面提到的C++編寫(xiě)的DLL例子中,如果去掉extern ”C”語(yǔ)句,C++會(huì)編譯出一些奇怪的函數(shù)名,原來(lái)的TestC函數(shù)會(huì)被命名為@TestC$s等等可笑的怪名字,這是由于C++采用了C++ name mangling技術(shù)。這個(gè)函數(shù)名在delphi中是非法的,我們可以這樣解決這個(gè)問(wèn)題:
改寫(xiě)引用函數(shù)為
functionstdcall;
external ’Cpp.dll’;name ’@TestC$s’;?????其中name的作用就是重命名。
2 、可把我們編寫(xiě)的DLL放到Windows目錄下或者Windows\system目錄下。這樣做可以在external語(yǔ)句中或LoadLibrary語(yǔ)句中不寫(xiě)路徑而只寫(xiě)DLL的名稱(chēng)。但這樣做有些不妥,這兩個(gè)目錄下有大量重要的系統(tǒng)DLL,如果您編的DLL與它們重名的話(huà)其后果簡(jiǎn)直不堪設(shè)想,況且您的編程技術(shù)還不至于達(dá)到將自己編寫(xiě)的DLL放到系統(tǒng)目錄中的地步吧!
六、在delphi中動(dòng)態(tài)調(diào)用DLL top
動(dòng)態(tài)調(diào)用DLL相對(duì)復(fù)雜很多,但非常靈活。為了全面的說(shuō)明該問(wèn)題,這次我們舉一個(gè)調(diào)用由C++編寫(xiě)的DLL的例子。首先在C++中編譯下面的DLL源程序。
#include
extern ”C” _declspec(dllexport)
int WINAPI TestC(int i)
{ ??????return i; ??????}
編譯后生成一個(gè)DLL文件,在這里我們稱(chēng)該文件為Cpp.dll,該DLL中只有一個(gè)返回整數(shù)類(lèi)型的函數(shù)TestC。為了方便說(shuō)明,我們?nèi)匀灰蒙厦娴恼{(diào)用程序,只是將原來(lái)的Button1Click過(guò)程中的語(yǔ)句用下面的代碼替換掉了。
procedure
type
TIntFunc=function(i:integer):integer;stdcall;
var
Th:Thandle; ?? Tp:TFarProc;
begin
Th:=LoadLibrary(’Cpp.dll’); {裝載DLL}
if Th>0 then
try
Tp:=GetProcAddress(Th,PChar(’TestC’));????//按函數(shù)名找,大小寫(xiě)敏感。
if Tp<>nil then
begin
Tf:=TIntFunc(Tp);
Edit1.Text:=IntToStr(Tf(1)); {調(diào)用TestC函數(shù)}
end
else ShowMessage(’TestC函數(shù)沒(méi)有找到’);
finally
FreeLibrary(Th); {釋放DLL}
end
else ????ShowMessage(’Cpp.dll沒(méi)有找到’);
end;
大家已經(jīng)看到了,這種動(dòng)態(tài)調(diào)用技術(shù)很復(fù)雜,但只要修改參數(shù),如修改LoadLibrary(’Cpp.dll’)中的DLL名稱(chēng)為’delphi.dll’就可動(dòng)態(tài)更改所調(diào)用的DLL。
一、定義所要調(diào)用的函數(shù)或過(guò)程的類(lèi)型
在上面的代碼中我們定義了一個(gè)TIntFunc類(lèi)型,這是對(duì)應(yīng)我們將要調(diào)用的函數(shù)TestC的。在其他調(diào)用情況下也要做同樣的定義工作。并且也要加上stdcall調(diào)用參數(shù)。
二、釋放所調(diào)用的DLL
我們用LoadLibrary動(dòng)態(tài)的調(diào)用了一個(gè)DLL,但要記住必須在使用完后手動(dòng)地用FreeLibrary將該DLL釋放掉,否則該DLL將一直占用內(nèi)存直到您退出Windows或關(guān)機(jī)為止。
現(xiàn)在我們來(lái)評(píng)價(jià)一下兩種調(diào)用DLL的方法的優(yōu)缺點(diǎn)。靜態(tài)方法實(shí)現(xiàn)簡(jiǎn)單,易于掌握并且一般來(lái)說(shuō)稍微快一點(diǎn),也更加安全可靠一些;但是靜態(tài)方法不能靈活地在運(yùn)行時(shí)裝卸所需的DLL,而是在主程序開(kāi)始運(yùn)行時(shí)就裝載指定的DLL直到程序結(jié)束時(shí)才釋放該DLL,另外只有基于編譯器和鏈接器的系統(tǒng)(如delphi)才可以使用該方法。動(dòng)態(tài)方法較好地解決了靜態(tài)方法中存在的不足,可以方便地訪(fǎng)問(wèn)DLL中的函數(shù)和過(guò)程,甚至一些老版本DLL中新添加的函數(shù)或過(guò)程;但動(dòng)態(tài)方法難以完全掌握,使用時(shí)因?yàn)椴煌暮瘮?shù)或過(guò)程要定義很多很復(fù)雜的類(lèi)型和調(diào)用方法。對(duì)于初學(xué)者,筆者建議您使用靜態(tài)方法,待熟練后再使用動(dòng)態(tài)調(diào)用方法。
1 把你的Form Uses到Dll中,你的Form用到的關(guān)聯(lián)的單元也要Uses進(jìn)來(lái)[這是最麻煩的一點(diǎn),因?yàn)槟愕腇orm或許Uses了許多特殊的單元或函數(shù)]
2 傳遞一個(gè)Application參數(shù),用它建立Form.
在DLL中建立一個(gè)TMDIChildForM
1 Dll中的MDIForm.FormStyle不用為fmMDIChild.
2 在CreateForm后寫(xiě)以下兩句:
functionstdcall
var
Form1: TForm1;????
begin
ptr:=@(Application.MainForm);????//先把dll的MainForm句柄保存起來(lái),也無(wú)須釋放,只不過(guò)是替換一下
ptr^:=LongInt(mainForm);?????????//用主調(diào)程序的mainForm替換DLL的MainForm。MainForm是特殊的WINDOW,它專(zhuān)門(mén)管理//Application中的Forms資源.為什么不直接Application.MainForm := mainForm,因//為Application.MainForm是只讀屬性
Form1:=TForm1.Create(mainForm);??//用參數(shù)建立
end;
備注:參數(shù)是主調(diào)程序的Application.MainForm
Delphi中高級(jí)DLL的編寫(xiě)和調(diào)用
根據(jù)Delphi提供的有關(guān) DLL編寫(xiě)和調(diào)用的幫助信息,你可以很快完成一般的 DLL編寫(xiě)和調(diào)用的 應(yīng)用程序。本文介紹的主題是如何編寫(xiě)和調(diào)用能夠傳遞各種參數(shù)(包括對(duì)象實(shí)例)的 DLL。例如, 主叫程序傳遞給 DLL一個(gè)ADOConnection 對(duì)象示例作為參數(shù), DLL中的函數(shù)和過(guò)程調(diào)用通過(guò)該對(duì)象 實(shí)例訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)。
需要明確一些基本概念。對(duì)于 DLL,需要在主程序中包含 exports子句,用于向外界提供調(diào)用 接口,子句中就是一系列函數(shù)或過(guò)程的名字。對(duì)于主叫方(調(diào)用 DLL的應(yīng)用程序或其它的 DLL), 則需要在調(diào)用之前進(jìn)行外部聲明,即external保留字指示的聲明。這些是編寫(xiě) DLL和調(diào)用 DLL必須 具備的要素。
另外需要了解Object Pascal 中有關(guān)調(diào)用協(xié)議的內(nèi)容。在Object Pascal 中,對(duì)于過(guò)程和函數(shù) 有以下五種調(diào)用協(xié)議:
指示字 參數(shù)傳遞順序 參數(shù)清除者 參數(shù)是否使用寄存器
register 自左向右 被調(diào)例程 是
pascal 自左向右 被調(diào)例程 否
cdecl 自右向左 調(diào)用者 否
stdcall 自右向左 被調(diào)例程 否
safecall 自右向左 被調(diào)例程 否
這里的指示字就是在聲明函數(shù)或過(guò)程時(shí)附加在例程標(biāo)題之后的保留字,默認(rèn)為register,即是 唯一使用 CPU寄存器的參數(shù)傳遞方式,也是傳遞速度最快的方式;
pascal: 調(diào)用協(xié)議僅用于向后兼容,即向舊的版本兼容;
cdecl: 多用于 C和 C++語(yǔ)言編寫(xiě)的例程,也用于需要由調(diào)用者清除參數(shù)的例程;
stdcall: 和safecall主要用于調(diào)用Windows API 函數(shù);其中safecall還用于雙重接口。
在本例中,將使用調(diào)用協(xié)議cdecl ,因?yàn)楸徽{(diào)用的 DLL中,使用的數(shù)據(jù)庫(kù)連接是由主叫方傳遞 得到的,并且需要由主叫方處理連接的關(guān)閉和銷(xiāo)毀。
下面是 DLL完整源程序和主叫程序完整源程序。包括以下四個(gè)文件:
Project1.DPR {主叫程序}
Unit1.PAS {主叫程序單元}
Project2.DPR {DLL}
Unit2.PAS {DLL單元}
{---------- DLL 主程序 Project2.DPR ----------}
library Project2;
uses
SysUtils,
Classes,
Unit2 in ‘Unit2.pas‘ {Form1};
{$R *.RES}
{ 下面的語(yǔ)句用于向調(diào)用該 DLL的程序提供調(diào)用接口 }
exports
DoTest; { 過(guò)程來(lái)自單元Unit2 }
begin
end.
{---------- DLL中的單元 Unit2.PAS ----------}
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Db, ADODB, StdCtrls, Menus;
type
TForm1 = class(TForm)
ADOConnection1: TADOConnection;{ 本地?cái)?shù)據(jù)庫(kù)連接 }
Memo1: TMemo; { 用于顯示信息 }
private
public
end;
{ 該過(guò)程向外提供 }
procedure DoTest(H: THandle; { 獲得調(diào)用者的句柄 }
AConn: TADOConnection;{ 獲得調(diào)用者的數(shù)據(jù)庫(kù)連接 }
S: string; { 獲得一些文本信息 }
N: Integer); { 獲得一些數(shù)值信息 }
cdecl; { 指定調(diào)用協(xié)議 }
implementation
{$R *.DFM}
procedure DoTest(H: THandle; AConn: TADOConnection; S: string; N: Integer);
begin
Application.Handle := H; { 將過(guò)程的句柄賦值為調(diào)用者的句柄 }
{ 上面語(yǔ)句的作用在于, DLL的句柄和調(diào)用者的句柄相同,在任務(wù)欄中就不會(huì) }
{ 各自出現(xiàn)一個(gè)任務(wù)標(biāo)題了。 }
with TForm1.Create(Application) do try{ 創(chuàng)建窗體 }
Memo1.Lines.Append(‘成功調(diào)用‘); { 顯示一行信息 }
ADOConnection1 := AConn; { 獲得數(shù)據(jù)庫(kù)連接的實(shí)例 }
Memo1.Lines.Append(
ADOConnection1.ConnectionString +
‘ - ‘ + S + ‘ - ‘ + IntToStr(N)); { 根據(jù)得到的參數(shù)顯示另一行信息 }
ShowModal; { 模式化顯示窗體 }
finally
Free; { 調(diào)用結(jié)束時(shí)銷(xiāo)毀窗口 }
end;
end;
end.
{---------- 調(diào)用者 Project1.DPR,很普通的工程文件 ----------}
program Project1;
uses
Forms,
Unit1 in ‘Unit1.pas‘ {Form1};
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
{---------- 調(diào)用者單元Unit1.PAS ----------}
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Db, ADODB;
type
TForm1 = class(TForm)
Button1: TButton; { 按此按鈕進(jìn)行調(diào)用 }
ADOConnection1: TADOConnection; { 本地?cái)?shù)據(jù)庫(kù)連接,將傳遞給 DLL }
procedure Button1Click(Sender: TObject);{ 調(diào)用 DLL}
private
public
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{ 外部聲明必須和 DLL中的參數(shù)列表一致,否則會(huì)運(yùn)行時(shí)錯(cuò)誤 }
procedure DoTest(H: THandle; { 傳遞句柄 }
AConn: TADOConnection; { 傳遞數(shù)據(jù)庫(kù)連接 }
S: string; { 傳遞文本信息 }
N: Integer); { 傳遞數(shù)值信息 }
cdecl; { 指定調(diào)用協(xié)議 }
external ‘Project2.dll‘;{ 指定過(guò)程來(lái)源 }
{ 調(diào)用過(guò)程 }
procedure TForm1.Button1Click(Sender: TObject);
begin
DoTest(Application.Handle,
ADOConnection1,
‘Call OK‘,
256);
end;
end.
Delphi中高級(jí)DLL的編寫(xiě)和調(diào)用(2)
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Db, ADODB, StdCtrls, Menus;
type
TForm1 = class(TForm)
ADOConnection1: TADOConnection;{ 本地?cái)?shù)據(jù)庫(kù)連接 }
Memo1: TMemo; { 用于顯示信息 }
private
public
end;
{ 該過(guò)程向外提供 }
procedure DoTest(H: THandle; { 獲得調(diào)用者的句柄 }
AConn: TADOConnection;{ 獲得調(diào)用者的數(shù)據(jù)庫(kù)連接 }
S: string; { 獲得一些文本信息 }
N: Integer); { 獲得一些數(shù)值信息 }
cdecl; { 指定調(diào)用協(xié)議 }
implementation
{$R *.DFM}
procedure DoTest(H: THandle; AConn: TADOConnection; S: string; N: Integer);
begin
Application.Handle := H; { 將過(guò)程的句柄賦值為調(diào)用者的句柄 }
{ 上面語(yǔ)句的作用在于, DLL的句柄和調(diào)用者的句柄相同,在任務(wù)欄中就不會(huì) }
{ 各自出現(xiàn)一個(gè)任務(wù)標(biāo)題了。 }
with TForm1.Create(Application) do try{ 創(chuàng)建窗體 }
Memo1.Lines.Append(‘成功調(diào)用‘); { 顯示一行信息 }
ADOConnection1 := AConn; { 獲得數(shù)據(jù)庫(kù)連接的實(shí)例 }
Memo1.Lines.Append(
ADOConnection1.ConnectionString +
‘ - ‘ + S + ‘ - ‘ + IntToStr(N)); { 根據(jù)得到的參數(shù)顯示另一行信息 }
ShowModal; { 模式化顯示窗體 }
finally
Free; { 調(diào)用結(jié)束時(shí)銷(xiāo)毀窗口 }
end;
end;
end.
{---------- 調(diào)用者 Project1.DPR,很普通的工程文件 ----------}
program Project1;
uses
Forms,
Unit1 in ‘Unit1.pas‘ {Form1};
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
{---------- 調(diào)用者單元Unit1.PAS ----------}
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Db, ADODB;
type
TForm1 = class(TForm)
Button1: TButton; { 按此按鈕進(jìn)行調(diào)用 }
ADOConnection1: TADOConnection; { 本地?cái)?shù)據(jù)庫(kù)連接,將傳遞給 DLL }
procedure Button1Click(Sender: TObject);{ 調(diào)用 DLL}
private
public
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{ 外部聲明必須和 DLL中的參數(shù)列表一致,否則會(huì)運(yùn)行時(shí)錯(cuò)誤 }
procedure DoTest(H: THandle; { 傳遞句柄 }
AConn: TADOConnection; { 傳遞數(shù)據(jù)庫(kù)連接 }
S: string; { 傳遞文本信息 }
N: Integer); { 傳遞數(shù)值信息 }
cdecl; { 指定調(diào)用協(xié)議 }
external ‘Project2.dll‘;{ 指定過(guò)程來(lái)源 }
{ 調(diào)用過(guò)程 }
procedure TForm1.Button1Click(Sender: TObject);
begin
DoTest(Application.Handle,
ADOConnection1,
‘Call OK‘,
256);
end;
評(píng)論