打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Bpl的使用以及与Dll的区别

Bpl的使用以及与Dll的区别

分类: Delphi技术 1672人阅读 评论(1) 收藏 举报

Bpl就是Dll,这句话当然没错。
  简单的例子,当然可以做,而且可以做得很好,我就曾把左兄的离线浏览器改成了这样一
个版本: 实现多个数据连接方式,ADO、DOA、ODAC、IBX、KAO,每个连接对应它的特定数据
库,都以一个独立的BPL来实现;主程序没有任何连接数据库的代码,主窗体切换版本时只要
这样一段代码就行了:
class function TfrmMain.SelectVersion(AReadSettings: Boolean): Boolean;
var
  DMPkg, DMClassName: string;
  I: Integer;
  UserCanceled: Boolean;
begin
  Result := False;
  repeat
    DMPkg := 'DoaDmDLL.bpl';
    //简单版本,直接提示用户输入bpl的名称
    UserCanceled := not InputQuery(Application.Title, 'ModuleName:', DMPkg);

    DMClassName := GetPackageDescription(PChar(DMPkg));
    I := Pos('[RICHEXPRDM]', DMClassName);
    try
      DM := nil;
    except
    end;
    //是正确的BPL,则开始载入
    if I > 0 then
    begin
      //取得Datamodule的类名
      DMClassName := Trim(Copy(DMClassName, I + 12, MAX_PATH));
      //卸载当前的BPL
      UnloadCurPkg;
      //用LoadPackage载入刚刚选择的BPL
      FHandle := LoadPackage(DMPkg);
      //创建Datamodule的实例
      DM := TDMClass(FindClass(DMClassName)).Create(Application);
      Result := True;
    end
    else
      Application.MessageBox('非法的DLL', '', MB_OK or MB_ICONERROR);
  until Result or UserCanceled;
end;
然后在DoaDM.pas里加这两句:
initialization
  RegisterClass(TDoaDM);

finalization
  UnregisterClass(TDoaDM);
编译成一个BPL就OK了。
这样用BPL做起来真的是非常简便,只要用LoadPackage以后,各单元就跟你直接uses差不多
了,initialization及finalization也会帮你执行,然后你就当做不存在BPL这回事一样用
就OK了。 这是BPL的好处,可以很方便的划分现有的代码成相对独立的模块。

但是,它也有几个致命的缺点,首先它无法象DLL一样,把它引用到的某一个或某几个BPL里的内
容编译进去,也就是说,只要你这个BPL引用了另一个BPL,发布的时候就必须两个BPL一起发
布,另外,只要一个BPL引用了某一个单元,其他的BPL就不能直接引用那个单元编译,而必须
引用一个公用的包含此单元的BPL,否则你这两个BPL是无法同时Load的。有了这两点,对于
大型项目来说,做起来是非常复杂的,各个模块之间的关系很难处理好,不象DLL,编译出
来后,各成各的体系,不会说因为这个DLL引用了这个单元,另一个DLL必须通过其他引用了
此单元的DLL来引用那个单元。

 Delphi中Package是一件非常强大的工具,用的好可以起到非常清晰的
划分程序结构的作用。因为他内建描述信息,可以和当前代码无缝集成起来,
可以保护包括类在内的任何元素,相当于VC中的MFC Extension DLL的作用。
但是一直以来的文章都只介绍静态连接的方法,这其实限制了Package的使用
因为静态连接的Package失去了其灵活性,可配置性等等。至于通过函数入口
方式访问,实在是大材小用,那不如直接用DLL还方便一些。
   如何动态载入Package,使用其中的类、函数、变量等等?起始说穿了很
简单,就是做一个代理包。因为在一个Delphi程序中,每个unit只能存在
一份,否则发生冲突。要动态载入包,又得取得其中信息,又不能直接uses
包含信息的unit(否则引起冲突),解决办法是另外建一个代理包来作为桥梁
传递信息。下面是一个简单的例子,主程序使用到两个包,DemoPak包中有
一个简单的Form;RegPak是所谓的代理包,起到注册信息的作用。
主程序对RegPak静态使用(在Project Options里面设置了),对DemoPak
动态载入(通过LoadPackage),而DemoPak依赖于RegPak(requires),
并在初始化时向代理包RegPak注册自己的可用类,这里举例注册类信息,
你可以方便的改成注册其他信息

unit FormReg;

interface

uses
  Classes, SysUtils, Forms, Contnrs;

type
  TFormClass = class of TForm;

procedure RegisterFormClass(const AName: string; const AFormClass: TFormClass);
procedure UnregisterFormClass(const AName: string);
function FindFormClass(const AName: string): TFormClass;

implementation

var
  g_lstNames: TStringList;
  g_lstForms: TClassList;

procedure RegisterFormClass(const AName: string; const AFormClass: TFormClass);
begin
  g_lstNames.Add(AName);
  g_lstForms.Add(AFormClass);
end;

procedure UnregisterFormClass(const AName: string);
var
  Index: Integer;
begin
  Index := g_lstNames.IndexOf(AName);
  if Index <> -1 then
  begin
    g_lstNames.Delete(Index);
    g_lstForms.Delete(Index);
  end;
end;

function FindFormClass(const AName: string): TFormClass;
var
  I: Integer;
begin
  for I := 0 to g_lstNames.Count - 1 do
  begin
    if g_lstNames[I] = AName then
    begin
      Result := TFormClass(g_lstForms.Items[I]);
      Exit;
    end;
  end;
  Result := nil;
end;

initialization
  g_lstNames := TStringList.Create;
  g_lstForms := TClassList.Create;

finalization
  FreeAndNil(g_lstForms);
  FreeAndNil(g_lstNames);

end.

   以上是RegPak的主要代码,因为举例,代码很简陋。主要思想就是保存注册信息,
提供查询方法。让我们看看在DemoPak中的使用

unit AboutForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TfrmAbout = class(TForm)
    lblAbout: TLabel;
  private
  public
  end;

var
  frmAbout: TfrmAbout;

implementation

uses FormReg;

{$R *.dfm}

initialization
  RegisterFormClass(TfrmAbout.ClassName, TfrmAbout);

finalization
  UnregisterFormClass(TfrmAbout.ClassName);
 
end.

   在初始化时向RegPak的FormReg单元提交自己的类信息,因为每个Package在载入时
无论动态静态都会自动初始化,而RegPak被主程序静态引用,肯定已经初始化,所以直接
注册即可,非常简单。最后看看主程序中的使用

uses
  FormReg;

procedure TfrmMain.btnAboutClick(Sender: TObject);
var
  hModule: THandle;
begin
  hModule := LoadPackage('DemoPak.bpl');
  try
    with FindFormClass('TfrmAbout').Create(nil) do
    try
      ShowModal;
    finally
      Free;
    end;
  finally
    UnloadPackage(hModule);
  end;
end;

    动态载入需要的包,查询需要的类的信息,使用之,最后卸载包。

象dll样输出 我已试成功了,而且是在mdi中试成功的。
贴出代码,大家同喜。希望bbkxjy来拿分。
主要的原因是bpl输出的目录默认为/bpl下。
而我一直没有在意。
bpl部分;
...
type
  TUIPackageForm = class(TForm)
    DataSource1: TDataSource;
    DBNavigator1: TDBNavigator;
    DBGrid1: TDBGrid;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  procedure ShowMDIChildForm(MainApp:TApplication);stdcall;
  exports
     ShowMDIChildForm;
var
  UIPackageForm: TUIPackageForm;
implementation
{$R *.DFM}
procedure ShowMDIChildForm(MainApp:TApplication);stdcall;
begin
  Application:=MainApp;
  UIPackageForm:=TUIPackageForm.Create(MainApp);
  UIPackageForm.Show;
end;
procedure TUIPackageForm.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  Action := cafree;
end;
动态调用的主程序部分。
type
  Tshowmdiform=procedure(MainApp:TApplication);stdcall;

  showmdiform:Tshowmdiform;
begin
  UIConnect := LoadPackage('bpl');
  showmdiform:=getprocaddress(UIConnect,'ShowMDIChildForm');
  if (@showmdiform<>nil) then
      showmdiform(application)
    else showmessage('no prc');
end;

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
关于bpl的一些讨论
Delphi 中的自动释放策略
无痛使用Delphi Package
A class named TcxRect already exists
学习使用Delphi 2009 开发 Word 2003 插件 <一>
delphi版inf方式加载驱动
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服