打开APP
userphoto
未登录

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

开通VIP
关于bpl的一些讨论

关于bpl的一些讨论

分类: Delphi 1860人阅读 评论(0) 收藏 举报

这些讨论来自于http://www.delphibbs.com/delphibbs/dispq.asp?lid=642584,正好我对bpl的结构,装载很糊涂,暂且摘录于下。

问题:动态调用bpl,如何调用bpl中的procedure和function ( 积分:200, 回复:39, 阅读:1320 )
分类:Object Pascal ( 版主:menxin, cAkk )
来自:maming, 时间:2001-9-24 17:54:00, ID:642584[显示:小字体 | 大字体]
查找bpl发现有人提过这方面的问题,没有得到正确的答案。
我再出200分求解。
你要是看了
http://www2.inprise.com.tw/tw/reference9.html
你就不会以为这是个小问题了。
来自:Kingron, 时间:2001-9-24 18:21:00, ID:642632
//不敢说话,听听高手意见。
来自:maming, 时间:2001-9-24 18:35:00, ID:642647
我是这样认为的,无所谓高手,也许你的一个建议也是你认为的高手们无法想到的,
所以请大家不多多的说出自己的想法,不管做得到做不到。谢谢先。
来自:sharkHun, 时间:2001-9-24 19:19:00, ID:642703
动态调用?不明白
来自:maming, 时间:2001-9-24 19:27:00, ID:642715
就是在要用的时候才调入内存。
来自:wangxd, 时间:2001-9-24 21:59:00, ID:642913
看看
来自:京工之鸟, 时间:2001-9-25 10:49:00, ID:643500
我试过了,验证了我的理解。BPL不可以用作DLL来输出函数的。
只能输出类,如果你一定要这样调用的话,可以在BPL里面定义
一个类,通过输出他的方法来实现。
来自:京工之鸟, 时间:2001-9-25 10:57:00, ID:643528
这个问题和李维的文章有什么关系呢?李维文章里面什么都没讲啊?
包最终是BORLAND用来解决类的问题的,不是用来输出函数的。输出
函数或过程的话只能用运行期包,由DELPHI自己负责管理调用。
我想不出来为什么一定要动态调用包,而且一定要输出过程呢?实在
不行你可以输出一个接口类嘛,这个接口类专门用来输出你的过程就
好了。我在做模块化的时候就是这么用的。在包里面定义一个接口类,
这个接口类有几个比如版本等属性,然后有一些需要输出的方法。在
调用的主程序里,创建一个接口类的实例,就可以用这些方法了。
来自:狐狸精, 时间:2001-9-25 12:12:00, ID:643722
这是没事找事做呀?我觉得没必要这么做啊。
来自:maming, 时间:2001-9-25 12:33:00, ID:643767
to 京工之鸟:
  你的想法就是我当初的想法,我这样做过,但觉得很麻烦。你做过还有源程序吗?
给个简单的我学习,多谢先。
  有人说可以象dll样输出的话,我想比用interface要简单些和灵活些。
来自:maming, 时间:2001-9-25 15:01:00, ID:644189
象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;
过几天就散分,让那些想知道的朋友看看先。或许还会有更好的建议。
我还想知道"京工之鸟"兄的做法。因为那种技术要好些,虽然复杂点。
明天开始试,请京工之鸟兄多多帮忙。
来自:Kingron, 时间:2001-9-25 15:32:00, ID:644261
那么这个和DLL几乎没有区别啊?

P.S:怎么搞得?掉了好几封信件?E_Mail通知有的时候不灵了?
来自:autumn, 时间:2001-9-25 16:07:00, ID:644333
知道acitivex中如何使用mdi form 吗?
来自:maming, 时间:2001-9-25 16:46:00, ID:644405
to autumn:应该也一样的可以实现吧!变通变通。

bpl和dll有很大的区别,bpl只限于delphi程序,可静可动。
还有就是bpl中的类输出要方便些。
来自:bbkxjy, 时间:2001-9-25 20:48:00, ID:644794
maming 兄:
   我想 bpl 主要优势是输出 VCL 的类,以跨模块(exe, dll, bpl 等)使用 VCL。要输出
一般的函数和过程,可能普通 dll 要更通用一些。你可以从 export 表看到最简单的 bpl
也要包含 system.pas 的代码,即使根本没用到 VCL 的框架,而普通 dll 不会这样。
  
来自:京工之鸟, 时间:2001-9-26 8:28:00, ID:645136
现在的EMAIL通知好像经常丢诶?

我试过代码了,没有问题,谢谢!:)
来自:maming, 时间:2001-9-26 16:05:00, ID:645243
to 京工之鸟,bbkxjy:
   对于bpl的类输出我还只是理解一点点,我主要是实现以下功能:
不管是bpl还是dll中的form要是mdichild.这个已实现了,还有一个就是以前我问过的问题,
没有得到很好的答案,后来没有继续做了,这次想做好他,要实现的功能:
一个bplA中的datamodule的database联接到数据库;其他的bpl或是exe中的mdiform都使用bplA的
database.这样每一台机器不管启动多少个exe或是bpl都只有一个联接到数据库。exe调用bpl都
是动态的,我以前的都是用静态调用(直接uses),好占系统资源,因为公司有很多机器是老的没有
办法,只能改成动态调用(直接用loadpackage)。
    请两位帮忙,我想用两种途径实现,一种就是interface.一种就是bpl或dll的类输出;
后一种我想用bpl好些,因为有些病毒喜欢删除dll.如果用bpl要好些吧!我昨天试了类输出
好象在共用datamodule时很有些问题,具体还没有查出来。
    想问两位一下,一个exe注册(registclass)了一个"Ttestmodule"后,另一个exe是不是可以
用getclass()找得到,并且可以使用?
来自:京工之鸟, 时间:2001-9-26 9:34:00, ID:645298
两个EXE是不行的,类是属于一个进程的,在另一个进程里没有办法调用,不然,
我们就用不着BPL了。registclass是在进程内注册,BPL最主要实现的就是可以把
包内的类注册到主进程中。
来自:京工之鸟, 时间:2001-9-26 9:39:00, ID:645312
如果用动态链接库就不行,DELPHI会在DLL和EXE里,各建一套类,相同的
一个类比如都是BUTTON,一个在DLL里,一个在EXE里,就不可以用AS或IS
操作符。但在BPL里,就可以了。
你可以参考《DELPHI技术手册》一书。
来自:maming, 时间:2001-9-26 9:39:00, ID:645314
to 京工之鸟兄:
  我昨天试了,是不行。如果用interface 是否可以呢?就是所谓的com吧!
  
  
来自:京工之鸟, 时间:2001-9-26 11:29:00, ID:645564
我说的INTERFACE不是这个INTERFACE,不过,定义一个COM接口当然可以在别的
EXE里调用。没必要这么复杂吧?
我没看懂你真正的问题是什么?下面这句话什么意思?你以前用怎么样的静态调
用?现在又要改成什么样的动态调用?
“他们这间都是动态调用的,我以前的都是用静态调用,好占系统资源,因为公司
有很多机器是老的没有办法,只能改成动态调用。”
来自:maming, 时间:2001-9-26 16:09:00, ID:646250
我又改了上面几句不通的地方。
"定义一个COM接口当然可以在别的EXE里调用。没必要这么复杂吧?"
我觉得真的要做好的话用com是最好的选择,看看word多神气。
如果我们的应用程序都做成这样子的话,以后就很好维护了。

来自:flier, 时间:2001-9-27 0:03:00, ID:647128
   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;

    动态载入需要的包,查询需要的类的信息,使用之,最后卸载包。
    
    很简单吧 :) 起始很多东西都没有什么技术难度的,关键看你想不想得到 :)
来自:vc3000, 时间:2001-9-27 11:06:00, ID:647627
代理包的使用有画蛇添足之嫌...
来自:maming, 时间:2001-9-27 11:20:00, ID:647679
to flier:
  你说的这个方法很不错,可如果是mdiform要如何实现呢?我曾试过,行不通,
我之所以用输出procdure和function是想解决mdiform。
  
来自:flier, 时间:2001-9-27 11:55:00, ID:647770
To vc3000:
   为何是画蛇添足?你有什么更好的办法吗?以我所知,
要从一个包中输出类或者其他不能通过getprocaddress
取得的信息是很困难的,而只使用函数输出,会使包的
灵活性大大下降,不如直接使用dll

To maming:
    Delphi好像还没有什么不可能的,只有自己不知道的,
我把昨天的程序改了一下,显示mdi一点问题没有啊?

btw:mdi是已经淘汰了的技术,最好不要再用了,呵呵 :)

以下是代理包的注册程序

unit FormReg;

interface

uses
  Classes, SysUtils, Forms, Contnrs;

type
  TFormClass = class of TForm;

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

implementation

var
  g_lstNames: TStringList;
  g_lstForms: TClassList;

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

procedure UnregisterFormClass(const AFormClass: TFormClass);
var
  Index: Integer;
begin
  Index := g_lstNames.IndexOf(AFormClass.ClassName);
  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.

以下是DemoPak程序,ChildForm是一个FormStyle为fsMDIChild的Form
unit ChildForm;

interface

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

type
  TfrmChild = class(TForm)
    Label1: TLabel;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmChild: TfrmChild;

implementation

{$R *.dfm}

uses FormReg;

procedure TfrmChild.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caMinimize;
end;

initialization
  RegisterFormClass(TfrmChild);

finalization
  UnregisterFormClass(TfrmChild);

end.

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
{$R *.dfm}

uses FormReg;

initialization
  RegisterFormClass(TfrmAbout);

finalization
  UnregisterFormClass(TfrmAbout);

end.

以下是主程序,FormStyle为fsMDIForm

unit MainForm;

interface

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

type
  TfrmMain = class(TForm)
    mnuMain: TMainMenu;
    mnuFile: TMenuItem;
    mnuFileNew: TMenuItem;
    mnuHelp: TMenuItem;
    mnuHelpAbout: TMenuItem;
    procedure mnuHelpAboutClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure mnuFileNewClick(Sender: TObject);
  private
    m_hDemoPak: THandle;
    m_lstChildForms: TObjectList;

    function GetDemoPakHandle: THandle;

    property DemoPakHandle: THandle read GetDemoPakHandle;
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

uses
  FormReg;

function TfrmMain.GetDemoPakHandle: THandle;
begin
  if m_hDemoPak = 0 then
    m_hDemoPak := LoadPackage('DemoPak.bpl');

  Result := m_hDemoPak;
end;

procedure TfrmMain.mnuHelpAboutClick(Sender: TObject);
begin
  if DemoPakHandle <> 0 then
    with FindFormClass('TfrmAbout').Create(nil) do
    try
      ShowModal;
    finally
      Free;
    end;
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  m_hDemoPak      := 0;
  m_lstChildForms := TObjectList.Create;
end;

procedure TfrmMain.FormDestroy(Sender: TObject);
begin
  m_lstChildForms.Free;
  
  if m_hDemoPak <> 0 then
    UnloadPackage(m_hDemoPak);
end;

procedure TfrmMain.mnuFileNewClick(Sender: TObject);
begin
  if DemoPakHandle <> 0 then
    m_lstChildForms.Add(FindFormClass('TfrmChild').Create(nil));
end;

end.
来自:bbkxjy, 时间:2001-9-27 14:10:00, ID:648066
我觉得代理包没有必要。Classes.pas 中本来就通过 RegisterClass,UnRegisterClass 和
FindClass,GetClass 等例程提供了相同的类注册,查找机制。Classes.pas 被编译到 
Vclx0.bpl 中,这个核心包就相当于上述的代理包。
to maming:
  李维的例子很清楚的。你是不是遇到了在主 exe 中 FindClass(或 GetClass) 返回为 nil
的情况?如果是的话,你的主 exe 和被动态 Loaded 的 package 都必须选 "Build with
Runtime Package" 的编译选项,并且至少包括 VCLX0.bpl。这样 exe 和 bpl 才会使用
同一份 VCL 的代码,包括全局数据。RegisterClass 等就用了全局范围的数据 ClassList。
来自:flier, 时间:2001-9-27 17:22:00, ID:648485
To bbkxjy:
  我觉得你误解了我的意思。我之所以这么写,只是举例而已,为的是说明
如何和动态载入的包之间交换编译时未知的类型信息。你所说的RegisterClass
实际上也是使用的同样的原理,只是代理包是System所在的package而已
何况RegisterClass的使用有其限制,如注册的类必须是 TPersistent的子类等等
而且无法加入自己的信息,而且如果要用到函数怎么办?用到全局变量怎么办?
从灵活性,可维护性,可扩展性角度来看,如果你要动态加载包,建立自己的
代理机制是必须的,否则就得忍受诸多限制。

btw:其实vcl类似RegisterClass的使用太多了,自己翻翻VCL源码就知道了
    李维他这么做也只是个投机取巧的办法,RegisterClass根本不是为了
    使用动态包而设计的。
来自:maming, 时间:2001-9-27 18:03:00, ID:648576
to 上面两位: 感谢你们的帮助,让我开了窍了。两位的方法结全起来很好用。
  mdi已经没有人用了吗?那现在流行用sdi还是别的?
  是不是outlookexpress啊!我没有注意那是如何做的,可否说说?

  我正动态bpl调用在测试数据库方面的应用.
来自:bbkxjy, 时间:2001-9-29 18:30:00, ID:651833
to flier:
   我只是针对你的例子而言,对你给出的场景是不需要自己实现 RegisterClass 等的。
另外,你的代理包中少了一些东西,就是类似 classes.pas 中在 initialization,
finalization section 分别调用 RegisterModuleClass,UnRegisterModuleClasses 过程
那样,当 ClassList 中注册的类所属的模块被 UnLoad 时,需要从 ClassList 中删除这
些类,防止造成无效指针。李维的例子中也有类似的过程调用的。
来自:flier, 时间:2001-9-29 20:47:00, ID:651932
to bbkxjy:
   我在第一篇文章里应该已经说得很清楚了“因为举例,代码很简陋”
我之所以代码这么写,只是为了表述一个主要思想而已。如果是实际使用中
不可能这么简单,我以前一个BCB写的类似的动态载入包的程序中,光代理包
中的处理就有大几百行代码,怎么可能这么简陋?
    至于注册和注销类信息,我的代码DemoPak.bpl里是如此写的
initialization
  RegisterFormClass(TfrmAbout.ClassName, TfrmAbout);
finalization
  UnregisterFormClass(TfrmAbout.ClassName);

   
来自:京工之鸟, 时间:2001-9-30 8:49:00, ID:652236
为什么我收不到EMAIL通知啊???????????????????????????????????????????
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
:(
来自:maming, 时间:2001-10-9 8:39:00, ID:662293
时间太长了,给分了,多谢各位了。
来自:genedna, 时间:2002-4-3 16:39:00, ID:1021160
高手所见!
来自:djdsz, 时间:2002-7-24 1:35:00, ID:1219096
代理包确实有点画蛇添足的感觉,我的系统中主程序没有静态联接所谓的代理包,照样工作
得很好。也没有什么受限制。
输出函数或过程可以通过消息传递函数或过程指针实现。
来自:hason, 时间:2002-8-13 18:45:00, ID:1262158
我想再求助一下
我已經產生了 abc.bpl 文件
有主程序 main.exe 調用  abc.bpl 在我本機上執行通過
但我把 main.exe,abc.bpl  copy 到另外一台電腦 c:/main/ 時
執行就不能動態調入 abc.bpl 不知為什么? 我把 abc.bpl copy 到$windows/system/
也是一樣的問題. 
来自:maming, 时间:2002-8-14 9:20:00, ID:1263015
to hason:
 你如果采用运行时包,那么还得那些你用到的*.bpl一起copy过去;
在delphi中运行程序,然后用那个module查看,用了那一些bpl.
来自:hason, 时间:2002-8-14 15:11:00, ID:1264025
噢!少了(rtl60.bpl) file
OK
TKS : maming
来自:wiseinfo, 时间:2003-1-10 11:06:00, ID:1570964
有没有完整的DEMO!!
来自:yyanghhong, 时间:2003-1-30 4:11:00, ID:1604549
http://www.playicq.com/dispdoc.php?t=27&id=2801
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Bpl的使用以及与Dll的区别
A class named TcxRect already exists
Delphi 中的自动释放策略
DELPHI下自定义包的作用,开发以及包文件的安装配置位置
如何用Delphi制作BPL包
delphi中的各种文件类型介绍【转】
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服