作者:Deven Tzu
日期:Sep-3-2001
相信很多使用Delphi的人都有想过将自己的应用系统分割成好几个Package, 这样的好处是可以只更新单一的Package (.BPL)就可以了, 而且.BPL还有一个.DLL没有的好处, 所有的.BPL 可以有一份大家共用的记忆区块(变数, function, procedure 等), 使用上就很方便了, 但是事实上很多人都因为Package使用上的限制而放弃(我曾经就是一个) , 现在我就来介绍Delphi Package的使用方法(不过实际处理方式请参考程式码, 本说明不多作说明):
简介
Package 和"斯斯" 一样分为二种:
静态载入:
一般大家在用Delphi时都是使用『静态载入』, 像VCL的Package就是这个方式, 这个方式的好处是设计者不用去理会Package 的载入及释放, 其实设计者根本感觉不到有使用这项技术; 当然您也可以手动将Package挂上系统(project->Options->Packages->Build with runtime packages中加入, 记得Package Name彼此的分隔符号是『;』)
动态载入:
至于『动态载入』当然就和『静态载入』相反, 不论是载入及释放都要自己来处理, 看起来好像『动态载入』一无是处, 其实也不是这样, 『动态载入』可以作到要使用时才载入, 有点像PnP (随插即用)的功能, 这是『静态载入』作不到的.
当然如果您高兴的话二种混用也是很好的方式, 也算是各取其优点来使用(本范例就是使用此方式)
限制
- 有些情形下使用Package只能间接参考的方式取得资料(变数, 元件, 物件…).
- Package Name 不能重覆.
- Contains 中的Unit Name 不能在『所有』的Package中重覆出现(只能出现一次).
- PackageA有使用到PackageB必需要在Requires中引用其.dcp 档(Unit及Package的Head File, C++使用.DLL不是也要Include Head File吗?), 但是PackageA及PackageB不能彼此循环引用.
范例说明
本范例主要目的是要让程式可以像"独立执行档" (就是系统程式都是在一个Proejct中未分割)一样, 在设计时可以直接引用参考另一个单元(Unit)的东西, 和原来的写法没有什么不同, 不过最好是作成二个Project 一个是Design Time 版本(不分割系统), 一个是分发版本(分割系统), 这样Debug会比较方便些; 本范例是改自蔡焕麟先生所写pkgdemo2. zip
首先要使用Package必需将project->Options->Packages->Build with runtime packages的选项勾选, 再将共用的Package Name 加入(如图一)
(图一)
然后在Requires 中引用使用到的Package dcp 档(如图二)
(图二)
选Options->Description->Runtime only (如图三), 因为我们是将系统分割不用Design Time Package , 如果选Designtime and runtime也可以不过会在系统上留一堆Design Time package并没有意义, 而且会比较占用记忆体; 至于Designtime only 就不用说了吧! 您的应用系统应该是要run的吧!
(图三)
Package1 有使用到Appaddin及PkgDATA所以在Requires 中引用(如图四)
图四)
Package1 中只有一个TForm1(可以放很多的TForm就不用我多说了吧), 内容如下:
unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, DBGrids, Db, DBTables; type TForm1 = class(TForm) Label1: TLabel; btnForm2: TButton; Edit1: TEdit; DBGrid1: TDBGrid; btnGetField: TButton; btnGetVar: TButton; procedure btnForm2Click(Sender: TObject); procedure Edit1Exit(Sender: TObject); procedure FormActivate(Sender: TObject); procedure btnGetFieldClick(Sender: TObject); procedure btnGetVarClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation uses PkgUtils, DATA; {$R *.DFM} { The following call Registers the addin with the application. Once this occurs the application can create instances of this form. } procedure TForm1.btnForm2Click(Sender: TObject); begin inherited; LoadAddinPackage('Package2', 'Package2.bpl'); ShowModalFormByClassName('TForm2'); end; procedure TForm1.Edit1Exit(Sender: TObject); begin COMPANY_NO := Edit1.Text; // 对共用变数处理 end ; procedure TForm1.FormActivate(Sender: TObject); begin Edit1.Text := COMPANY_NO; // 对共用变数处理 end ; procedure TForm1.btnGetFieldClick(Sender: TObject); var A: TTable; begin // 以下这二行是用间接参考方式对 TDataModule1 处理 , 比较麻烦 // A := FindTable('Table1'); // Edit1.Text := A.FieldByName('CustNo').AsString; // 对 TDataModule1 处理 Edit1.Text := DataModule1.Table1.FieldByName('CustNo').AsString; end ; procedure TForm1.btnGetVarClick(Sender: TObject); begin DataModule1.COMPANY_NAME := Edit1.Text; end ; initialization RegisterClass (TForm1); // 记得要用 GetClass 取得需在此注册 ( 提供 RTTI 资讯 ) end .
当然如果您实在懒得处理Package的载入及释放, 也可以全部的Package全部使用『静态载入』由Delphi帮您处理.Delphi的『动态载入』和『静态载入』似乎处理的方法不同, 『静态载入』可以和系统整合的很好(像本例), 但『动态载入』是使用LoadPackage 处理, 但是共用的部份却无法直接参考, 但是用『静态载入』却可以, 我还查不出为何有此差异.