在開(kāi)發(fā)復(fù)雜的應(yīng)用系統(tǒng)時(shí),把系統(tǒng)分為各個(gè)模塊分而制之是一種很平常的策略,這樣就可以使得每個(gè)模塊負(fù)責(zé)一套業(yè)務(wù)邏輯。模塊可以是一個(gè)DLL,在需要的時(shí)候,主程序可以調(diào)用這個(gè)DLL以使用該模塊。
當(dāng)你開(kāi)發(fā)一個(gè)MDI應(yīng)用程序,并希望把該應(yīng)用程序分割成各個(gè)模塊時(shí),自然會(huì)有這樣的疑問(wèn):“如何將MDI子窗體創(chuàng)建在DLL中,以使得在必要的時(shí)候能夠被主程序調(diào)用?”
如果你已經(jīng)知道如何在DLL中創(chuàng)建非MDI子窗體的窗體,并且也知道如何從主程序調(diào)用這個(gè)DLL中的窗體時(shí),你或許會(huì)認(rèn)為問(wèn)題已經(jīng)得到了解決,但事實(shí)并非如此。
如何讓DLL中的MDI子窗體得知哪個(gè)窗體是它的父窗體(以便讓其能夠以子窗體的形式展現(xiàn)出來(lái))?用于創(chuàng)建該子窗體的Application對(duì)象(位于父窗體所在的應(yīng)用程序中)與你在DLL中獲得的Application對(duì)象根本就是兩回事情,不僅如此,兩者的Screen對(duì)象也不相同。
想要在DLL中保存并創(chuàng)建MDI子窗體?沒(méi)門(mén)!
就如你所看到的這個(gè)標(biāo)題所說(shuō)的一樣,根本就沒(méi)有辦法在DLL中保存并創(chuàng)建MDI子窗體,然后讓MDI應(yīng)用程序去調(diào)用它?;蛘吣銜?huì)說(shuō),你已經(jīng)在網(wǎng)上找到一些資料,上面顯示如何保存并傳遞Application對(duì)象,以使得DLL中的MDI子窗體能夠被創(chuàng)建于正確的MDI父窗體中,其實(shí)這種做法并不奏效,至少在Delphi 5以上的版本中不會(huì)奏效。
那么解決方案是什么呢?
如果你非要在DLL中保存并創(chuàng)建MDI子窗體,那么你需要在主程序(MDI應(yīng)用程序)和DLL生成的時(shí)候,與運(yùn)行時(shí)包(runtime package)一起生成。這樣做就確保了主程序和DLL使用了共同的Application和Screen對(duì)象,并使用了相同的RTL與VCL實(shí)例。為了100%確保“安全”,你應(yīng)該使用包,而不是DLL。
包中的MDI子窗體:唯一正確的解決方案
Delphi的包是一種特殊的DLL,它僅用于Delphi的應(yīng)用程序。如果你的應(yīng)用程序模塊是使用包的形式開(kāi)發(fā)而不是DLL,那么所有的模塊都會(huì)共享同一個(gè)內(nèi)存管理機(jī)制,包括了VCL全局對(duì)象(例如Application和Screen)以及RTL與VCL在內(nèi)存中的同一代碼拷貝。
包中的MDI子窗體:實(shí)例
現(xiàn)在來(lái)看一個(gè)實(shí)例,首先,我們將創(chuàng)建一個(gè)MDI應(yīng)用程序:
1、 創(chuàng)建一個(gè)新的MDI應(yīng)用程序。你可以使用MDI應(yīng)用程序創(chuàng)建向?qū)В?/span>File - New - Other - Projects - MDI Application)
2、 確保主窗體的FormStyle屬性已經(jīng)設(shè)置為fsMDIForm
3、 添加一個(gè)MainMenu控件,使其只有一個(gè)用于從包中加載子窗體的菜單項(xiàng)
4、 確保在生成應(yīng)用程序的時(shí)候,是和運(yùn)行時(shí)包一起生成的。在Project – Options菜單中,選擇Packages選項(xiàng)卡,然后選中“Build with run-time packages”選項(xiàng)。你至少要選中rtl包和vcl包
在真正編碼前,首先生成該包,并且向其添加一個(gè)MDI子窗體
1、 創(chuàng)建一個(gè)新的運(yùn)行時(shí)包
2、 向包添加一個(gè)TForm對(duì)象,確保該對(duì)象的FormStyle屬性已經(jīng)設(shè)置為fsMDIChild
3、 添加一個(gè)導(dǎo)出過(guò)程,用于創(chuàng)建子窗體的實(shí)例:
procedure TPackageMDIChildForm.FormClose
(Sender: TObject;
var Action: TCloseAction);
begin
//since this is an MDI child, make sure
//it gets closed when the user
//clicks the x button.
Action := caFree;
end;
procedure ExecuteChild;
begin
TPackageMDIChildForm.Create(Application);
end;
exports
//NOTE!! The export name
//is CASE SENSITIVE
ExecuteChild;
end.
重新回到MDI主程序,以下是MDI主窗體的所有代碼:
type
//signature of the "ExecuteChild"
//procedure from the Package
TExecuteChild = procedure;
TMainForm = class(TForm)
...
private
PackageModule : HModule;
ExecuteChild : TExecuteChild;
procedure PackageLoad;
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
procedure TMainForm.PackageLoad;
begin
//try loading the package
//(let's presume it's in the same
//folder, where the main app. exe is)
PackageModule := LoadPackage('MDIPackage.bpl');
//if loaded, try locating
//the ExecuteChild procedure
if PackageModule <> 0 then
try
@ExecuteChild := GetProcAddress(PackageModule,
'ExecuteChild');
except
//display an error message if we fail
ShowMessage ('Package not found');
end;
end;
//menu click
procedure TMainForm.mnuCallFromDLLClick
(Sender: TObject);
begin
//lazzy load package
if PackageModule = 0 then PackageLoad;
//if the ExecuteChild procedure
//was found in the package, call it
if Assigned(ExecuteChild) then ExecuteChild;
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
//if the package was loaded,
//make sure to free the resources
if PackageModule <> 0 then
UnloadPackage(PackageModule);
end;
上面的代碼中,選中菜單項(xiàng)時(shí),主程序動(dòng)態(tài)地加載了所需的包(使用PackageLoad過(guò)程),并且在應(yīng)用程序終止的時(shí)候卸載了已經(jīng)加載的包。最后,在運(yùn)行時(shí),我們得到了一個(gè)能夠正確加載并運(yùn)行包中MDI子窗體的應(yīng)用程序。
最后需要說(shuō)明的是,在使用runtime package模塊化應(yīng)用程序時(shí),你必須將所需的包與應(yīng)用程序的exe文件一起發(fā)布。聯(lián)系客服