日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

转:以Delphi Package架构多人开发应用程序环境

發布時間:2023/12/16 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 转:以Delphi Package架构多人开发应用程序环境 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?????轉自https://www.cnblogs.com/dingjie/articles/195195.html

本人編寫的另一實現BPL窗體嵌入主程序的Demo發布在我的個人網站,blog.hsuzo.cn,該文后有源碼。

  • 印象中的Package

在一般的AP開發時,我們知道在Delphi7.0整合環境中將Project->Options->選到Packages卷標頁,Builder with runtime packages選項打勾,就會讓編譯出來的執行文件Size變小很多(以空白的Form1為例,編譯出來的Size由367kb變成20kb),因為它把一些VCL共享模塊的Loding放到*.bpl中;換句話說這個變小的EXE文件在執行時是需要那些*.bpl的,而原本較大的執行文件執行時則不需要那些*.bpl,這樣看來其實只是換湯不換藥罷了。

這種編譯架構的差異有什么優點?試想,如果Project1.exe&Project2.exe都不使用Builder with runtime packages選項,Project1.exe跟Project2.exe編譯出來的Size都是367kb,若有10個ProjectN.exe,則占用的Size將是全部exe加起來的總和,若是我們將其中共享的部分(如VCL共享模塊)獨立出來,每個EXE都將只有20K(以測試用的空白Form而言),缺點則只有每個exe執行時都要在系統搜尋路徑中找得到那些VCL的共享模塊(以*.bpl文件名存在);所以這種架構適用于一個項目系統內有多個獨立執行文件或獨立業務模塊存在的情況,多個執行文件就可以分散給多個人去開發,也可依照應用程序不同的業務性質去區隔并分別設計。

將執行文件分散開來開發,這種做法跟一個主執行文件配合多個不同業務別的DLL開發沒有什么差別,而降低每支執行文件的Size也只是Package最普通的應用;使用Package有一個更強大的優點——可以共享變量,如果將數據模塊當成一個共享變量的觀念去看待,我們不用在每次啟動或關閉不同的子系統業務模塊時,重新連接或釋放數據庫的Connection。不過,要真正能共享數據庫模塊是需要每個業務模塊皆以*.bpl的方式存在,這也是實際應用上的狀況。

項目架構種類

Package編譯方式

類型一

Project0.exe(MIS主系統)

Project1.exe(會計子系統)

Project2.exe(人事子系統)

Project3.exe(庫存子系統)

所有EXE文件編譯選項

□Builder with runtime

packages

1.每個子系統都是完整獨立的EXE文件;每個EXE文件至少都有數百KB以上

類型二

Project0.exe(MIS主系統)

Project1.exe(會計子系統)

Project2.exe(人事子系統)

Project3.exe(庫存子系統)

VCL共享模塊(*.bpl)

所有EXE文件編譯選項

■Builder with runtime

packages(打勾)

1.每個子系統雖然都是EXE文件,但執行時需要VCL的共享模塊存在;此種方式,每個EXE文件SIZE都只有幾十KB

2.此種架構只有節省檔案SIZE的優點

類型三

Project0.exe(MIS主系統)

Project1.bpl(會計子系統)

Project2.bpl(人事子系統)

Project3.bpl(庫存子系統)

VCL共享模塊(*.bpl)

DataMoudle(db.bpl)

所有EXE文件編譯選項

■Builder with runtime

packages(打勾)

1.將各子系統中共享的程序(如連接數據庫的模塊)獨立出來共享

2.除主系統外其余子系統皆為BPL型式存在

3.節省檔案SIZE外,并有共

用數據庫連結模塊的優點

注:

1.MIS主系統(Project0.exe)可能只是一個選單(Menu),用來當作啟動各子系統的Shell

2.VCL共享模塊(*.bpl)Delphi在安裝時即安裝至C:\WINNT\SYSTEM32\中的那些*.bpl檔案,不管你EXE文件是否Builderwithruntimepackages,那些*.bpl早已存在,端看你要不要用它而已(Builderwithruntimepackages是否打勾)

3.當AP開發是以Builderwithruntimepackages(打勾)編譯時,RELEASE到客戶端同時也要將C:\WINNT\SYSTEM32\中的那些*.bpl檔案COPY到客戶端,因為客戶端沒有安裝Delphi,所以沒有那些VCL共享模塊(*.bpl)

4.BPL跟EXE一樣是可以“LOAD”其它的*.bpl;如Project1.bpl(會計子系統)會”LOAD”VCL共享模塊(*.bpl)以及DataMoudle(db.bpl)

5.在類型三中,作為唯一的EXE文件,Project0.exe編譯時一定要Builderwithruntimepackages(打勾),否則在動態加載其它子系統模塊時,會出現找不到類別的錯誤

?

  • Package架構的優缺點

使用Package的優點:

1.類似DLL-應用程序可以被高度的模塊化

2.優于DLL之處-可以共享變量

使用Package的缺點:

1.架構較復雜,需要花比較多的心思在模塊化的設計

2.需要程度較高的開發人員,若對package的使用不熟,容易出trouble

?

  • 哪些東西可以開發成為Package

前面講過,Package其實就是類似DLL的一種架構,但是是專門屬于Borland C++Builder/Delphi使用的一種DLL架構,舉凡有含Form的Unit文件、無Form的Unit文件(如自己寫的函數庫)、組件等等,都可以編譯成為Package

Package檔案的類型

?

Package項目

相當于一般項目

相當于DLL項目

編譯前

*.DPK

Package項目文件

DPR文件

DPR文件

*.PAS

PackageSource

PAS文件(Source)

PAS文件(Source)

編譯后(產出)

*.DCP

Package產出文件

?

LIB文件

*.BPL

Package產出文件

EXE文件

DLL文件

Package中的DCP文件,跟DLL項目的LIB文件類似,DCP是提供為靜態連結編譯之用(注意:只是靜態連結的“編譯過程”用之),BPL則是直接提供為執行環境之用;DCP只有在提供給別的EXE或BPL靜態連結的“編譯過程”中會用到,所以不用RELEASE到客戶端;在執行環境中,Package不管是被靜態聯結或是動態連結使用,一律以BPL型式存在。

例如Project0.exe以“靜態連結”方式使用到Package1,Project0.exe在編譯時需要Package1.dcp的檔案存在,但編譯后分發到客戶端的檔案則為roject0.exe以及Package1.bpl(Package1.dcp不用release),雖然它們之間是使用靜態連結(加載)…

如果Project0.exe以“動態連結”方式使用到Package1,Project0在編譯過程中根本不需要任何Package1的檔案,而分發到客戶端一樣仍然只有roject0.exe以及Package1.bpl。

?

Package的載入(從EXE載入BPL的角度來看)

靜態加載–不管用得用不到,該*.dcp是一定都要加載的,也就是在exe項目選項中的Builder with runtime packages->Add加入,如Project0.exe(MIS主系統)一定要加載VCL共享模塊(*.dcp),否則無法產生GUI接口;所以就用靜態加載的方式加載它;靜態加載的*.dcp,是在編譯時就已決定的

動態加載–使用到時,才透過LoadPackage()這個API來呼叫,如Project0.exe(MIS主系統)呼叫各個子系統的*.bpl,是透過Coding的方式去加載子系統的*.bpl,Project0.exe(MIS主系統)并不需要在編譯時將所有子系統加載編譯

Package的載入(從BPL載入BPL的角度來看)

靜態加載–放在Package項目之Requires區段中的*.dcp文件,反正是“一定要用到”的,不需要利用動態加載的就放在這兒

動態加載–跟EXE動態加載BPL方式相同,使用到時,才透過Coding方式利用LoadPackage()這個API來加載的

?

  • Package項目的建立

在Delphi7.0整合環境中,主菜單->File->Close ALL關掉所有的Form及項目;然后再一次:主菜單->File->New->Other...->選擇Package,建立一個全新的Package項目

一個全新的Package應該是長的如下圖所示,分為兩個部分:Contains內放的是項目的主題,也就是我們要為這個Package開發的程序代碼;Require內放的是要“靜態連結” 的其它Package(*.dcp文件),也就是不需要利用LoadPackage()這個API來加載(動態加載)的Package就放到Require中;rtl.dcp是系統內建就已Require的Package,應該是VCL之類的東西吧

接下來我們要開始設計項目內容,幫這個Package項目加入一個空白的Form,到Delphi主菜單->File->New->Form;我們再回到Project Manager看看在Contains區段是否多了Unit1(Form1);前面說過,Contains內放的是項目的主題,也就是我們要為這個Package開發的程序代碼,接下來要在Form1中放什么組件,寫什么程序代碼,都跟一般的Form沒什么兩樣,只是若這個Form是寫在一般的項目中(Project1.dpr)它可以被編譯成EXE文件,而在Package項目中(Package1.dpk),它只能被編譯成Package1.dcp(給別的項目靜態連結編譯時用)和Package1.bpl

Package1.dcp以及Package1.bpl這兩個編譯后的結果,Default是放到Delphi7.0目錄的.\Projects\Bpl\中;若要改變,請至Delphi主菜單->Project->Options(如下圖),修改項目的輸出目錄路徑為目前路徑(.\)

還有,很重要的一點,在Package項目中的Form由于將來會被別人加載使用(不管被靜態或被動態加載);它都需要先向系統注冊它的類別;所以在前面的例子中,將Unit1(Form1)開發好后,最后要在end.之前插入注冊(RegisterClass)/注銷(UnRegisterClass)的程序代碼,注冊內容就是自己的Form類別(如TForm1、TForm2等等)

(其它程序代碼)............initializationRegisterClass(TForm1);finalizationUnRegisterClass(TForm1);end.

?

  • Package的應用限制

如果只是一個EXE文件附帶一個BPL文件,這種架構還算單純,但如果如文章開始所述:一個MIS主系統(Project0.exe)帶著多個子系統(*.bpl),那會有什么限制發生呢?

1.各個Package(*.bpl)在開發過程中,彼此的Contains區段中不能有同名的Unit

2.共享的unit一定要放在package,也就是要把共享模塊變成Package

我們現在來想想,如果是我們來主導這個系統,我們會如何設計呢?

1.雖然各項子系統是各自獨立開發,甚至是交由不同的開發TEAM來完成,但為了接口的風格一致及操作統一(如Button的大小及位置),我們會有一個共通的BaseForm的雛形,讓所有的子系統的主Form都由這個BaseForm繼承而來,這樣會讓子系統(Package)的Contains區段都會有一個共同uses的BaseForm.pas

2.為了程序代碼的一致性,也為了增加Coding速度,公司累積了程序代碼經驗,可能會有一個公用副函數集MySub供各個子系統呼叫,這樣也會讓子系統(Package)的Contains區段都會有一個共同uses的MySub.pas

為了不讓BaseForm.pas及MySub.pas成為Package開發的限制瓶頸,所以我們要將BaseForm及MySub也變成Package(成為BaseForm.dcp及MySub.dcp),然后讓各個子系統Package放在Requires中靜態連結編譯

?

  • Package的動態加載

前面已介紹了Package在靜態加載以及編譯的方法,現在要介紹Package應用的重頭戲-動態加載;一般來說,共通的副函數或共享分享的數據庫模塊以及共享的繼承樣板會先被制作成Package,然后被主程序(EXE文件)及各個子系統模塊(BPL文件)作為靜態連結之用;而整個系統開發的主角,也就是各個子系統模塊會被主程序或子系統模塊(BPL文件)間,當成動態加載的目標。

varModuleInstance1:HMODULE;{$R*.dfm}//---------------------------------------------------------------//動態加載Package//---------------------------------------------------------------procedureTForm0.Button1Click(Sender:TObject);beginModuleInstance1:=LoadPackage('Package1.bpl');end;//---------------------------------------------------------------//將Package中的Form1帶出//---------------------------------------------------------------procedureTForm0.Button2Click(Sender:TObject);varfrm?:?TcustomForm;beginfrm :=CreateFormByClassName('TForm1');tryfrm.ShowModal;finallyfrm.Release;end;end;//---------------------------------------------------------------//釋放Package//---------------------------------------------------------------procedureTForm0.Button3Click(Sender:TObject);beginUnloadAddInPackage(ModuleInstance1);end;

注:

1.在上列的程序代碼中,加載Package的LoadPackage()是系統內建函數

2.帶出Package中的Form資源或是釋放Package的函數由于要多做一些處理,所以把它包在CreateFormByClassName()以及UnloadAddInPackage()兩個自訂函數中

//---------------------------------------------------------------//自訂函數–CreateFormByClassName(),建立Form//---------------------------------------------------------------Function?TForm0.CreateFormByClassName(const ClassName:string) : TCustomForm;varAClass:TPersistentClass;beginAClass:=GetClass(ClassName);If?AClass=nil then?exit;Result:=TComponentClass(AClass).Create(Application)?as?TCustomForm;//或Result:=TCustomForm(TComponentClass(AClass).Create(Application));end;//---------------------------------------------------------------//自訂函數–CreateDataModuleByClassName(),建立數據模塊//---------------------------------------------------------------Function?TForm0.CreateDataModuleByClassName(const?ClassName: string):TDataModule;varAClass:TPersistentClass;beginResult:=nil;AClass:=GetClass(ClassName);If?AClass=nil then?exit;Result:=TComponentClass(AClass).Create(Application)?as?TDataModule;end;//---------------------------------------------------------------//自訂函數–UnloadAddInPackage(),釋放Package//---------------------------------------------------------------Procedure?TForm0.UnloadAddInPackage(ModuleInstance:HMODULE);vari:Integer;M:TMemoryBasicInformation;beginfor?i:=Application.ComponentCount-1?downto?0?dobeginVirtualQuery(GetClass(Application.Components[i].ClassName),M,SizeOf(M));if?(ModuleInstance=0)?or?(HMODULE(M.AllocationBase)=ModuleInstance)?thenApplication.Components[i].Free;end;//下面這兩個函數應該是只要取其中一個呼叫即可UnRegisterModuleClasses(ModuleInstance);//直接注銷PackageUnloadPackage(ModuleInstance);//間接注銷,呼叫Package中的finalization區段end;

?

?

  • 完整的Package項目架構范例

子系統名稱

部主要對象

補充

Project0.exe(MIS主系統)

Form0(Unit0.pas)

整個項目中唯一的EXE文件Project的Option選項要設

■Builderwithruntimepackages并加入BaseForm,MySub兩個DCP

(靜態連結)

Package1.bpl(會計子系統)

Form1(Unit1.pas)

Requires區段要加入

BaseForm,MySub兩個DCP

(靜態連結)

Package2.bpl(人事子系統)

Form2(Unit2.pas)

Requires區段要加入

BaseForm,MySub兩個DCP

(靜態連結)

Package3.bpl(庫存子系統)

Form3(Unit3.pas)

Requires區段要加入

BaseForm,MySub兩個DCP

(靜態連結)

MySub.bpl共享函數庫

無,只有函數程序代碼

MySub2003.pas

并提供MySub.dcp供其它系統靜態連結之用

BaseForm.bpl共享繼承Form雛形

FormBase

(UnitFormBase.pas)

并提供BaseForm.dcp供其它系統靜態連結之用

注:在此仍不厭其煩的提醒:每個Package的Source最后都要在end.之前插入注冊(RegisterClass)/注銷(UnRegisterClass)自己類別的程序代碼

?

  • Project0.exe(MIS主系統)

注:不管這個唯一的EXE主程序是否需要加入自訂的共享模塊(如本例的BaseForm,MySub兩個DCP)有的話,它仍然要將Builder with runtime packages打勾(就算只有使用那些系統定的VCLDCP),否則這個EXE文件執行后會無法動態加載其它的子系統模塊,會出現找不到類的錯誤,這個問題我花了一整個下午才找到問題原來在這里;但我不知原因是否為Delphi Bug;因為在我的認知,若不使用自訂的共享模塊(有的話也是放到靜態鏈接中),主程式跟其它的子系統間則只有動態加載的關系,那靜態鏈接則只有決定是否要將系統的VCL靜態鏈接進來的問題;Builder with runtime packages打不打勾,單純也只決定跟系統VCL間的關系,為何會影響其它子系統的動態加載??我不愿就看圖故事或湊答案的方式來牽強解釋這個問題原因….只有推論為Delphi bug

  • Package1.bpl(會計子系統)
  • Package2.bpl(人事子系統)
  • Package3.bpl(庫存子系統)

(略,以上類推)

注:

1.在Requires區段(靜態連結)中,除了手動加入的BaseForm,MySub兩個DCP外,其它DCP是由系統自動加入的,不用理會它

2.Package的Contains區段中,彼此間不能有相同名稱的Unit,也就是說Package2中的Unit不能跟Package1中一樣也叫Unit1(要改名如Unit2或其它)

  • MySub.bpl共享函數庫(無Form,只有函數庫的程序代碼MySub2003.pas)
  • BaseForm.bpl共享繼承Form雛形

?

?

  • Package項目的測試環境

Package在測試及除錯上由于BPL不能直接執行驗證結果,所以在開發中的測試是個問題;不過前面說過,除了項目文件架構不同外,Source開發式和一般Project項目是沒什么兩樣的,以前面介紹過的Package1.bpl(會計子系統)為例,我們來看一下如何開發及測試

1.建立一標準可編譯成EXE的Project1.dpr項目,加入Unit1(Form1),將項目寫好后直接編譯為EXE文件測試

2.當測試好后沒問題,新開啟一Package項目叫做Package1.dpk,將Contains區段納入剛才開發的Project1.dpr項目中的Unit1(Form1);此時重新編譯,會計子系統就變成一個BPL而不是EXE文件了

3.以后要修改會計子系統,就利用Project1.dpr項目來編譯成EXE測試,再用Package1.dpk項目來編譯成BPL

?

  • Package項目網狀加載問題

在先前介紹的系統中,各個子系統理應由Project0.exe(MIS主系統)這個唯一的EXE文件來切換進入或退出各子系統的時機;但是我們在實際的應用中,也可能發生直接由Package1.bpl(會計子系統)加載(跳到)Package3.bpl(庫存子系統)的情況(記得BPL也是可以加載BPL的吧?),整個系統RUN過一段時間后,誰加載誰,或誰已被退出,已經不是很清楚了,若此時突然有個快捷方式直接回主程序中并結束程序,我們要能保證所有曾經被加載的BPL都要被全部完全釋放干凈;如果動態加載其它BPL的LoadPackage()函數是分散在各子系統中(人人都可以加載其它的BPL),我們如何去統一管理那些PackageHandle變量呢(HMODULE)?所以理想的方法是將加載BPL的動作統一放在共同函數庫(如MySub中),主程序或各個子系統要加載或釋放BPL都透過在共通函數庫中包裝好的Package載入或釋放函數,該函數內部有List數組來記錄管理所有的PackageHandle的消長情況;參考資料“DelphiPackage學習筆記”及“DelphiPackage無痛使用”中有個寫好的共享函數庫,叫做PkgUtils.pas,您可以把它里面的函數合并到您公司開發的共享函數庫中(如MySub中),所有主程序或各個子系統的Package加載/釋放動作都透過這個公用函數庫包裝的函數去執行動作。

總結

以上是生活随笔為你收集整理的转:以Delphi Package架构多人开发应用程序环境的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。