分类:
Delphi
2005-08-29 11:06
1860人阅读
收藏
举报
这些讨论来自于http://www./delphibbs/dispq.asp?lid=642584,正好我对bpl的结构,装载很糊涂,暂且摘录于下。 | 来自: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!! |
| |
|