您的位置:首页 > 其它

Bpl的使用以及与Dll的区别

2005-04-17 16:34 411 查看
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;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: