Delphi Package无痛使用

时间:2021-09-21 17:41:13

作者: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 (随插即用)的功能, 这是『静态载入』作不到的. 

当然如果您高兴的话二种混用也是很好的方式, 也算是各取其优点来使用(本范例就是使用此方式) 

限制

  1. 有些情形下使用Package只能间接参考的方式取得资料(变数, 元件, 物件…).
  2. Package Name 不能重覆.
  3. Contains 中的Unit Name 不能在『所有』的Package中重覆出现(只能出现一次).
  4. 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 加入(如图一)

Delphi Package无痛使用

(图一)

然后在Requires 中引用使用到的Package dcp 档(如图二)

Delphi Package无痛使用

(图二)

选Options->Description->Runtime only (如图三), 因为我们是将系统分割不用Design Time Package , 如果选Designtime and runtime也可以不过会在系统上留一堆Design Time package并没有意义, 而且会比较占用记忆体; 至于Designtime only 就不用说了吧! 您的应用系统应该是要run的吧!

Delphi Package无痛使用

(图三)

Package1 有使用到Appaddin及PkgDATA所以在Requires 中引用(如图四)

Delphi Package无痛使用

图四)

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 处理, 但是共用的部份却无法直接参考, 但是用『静态载入』却可以, 我还查不出为何有此差异.