package(转)
来源:百度文库 编辑:神马文学网 时间:2024/04/28 09:48:52
不知道各位用Delphi写数据库程序的朋友有没有碰到过这样的问题:写出来的
程序体积太庞大!我写的上一个项目中可执行文件竟然达到4.3M!很可怕的体积!
这样不仅分发程序比较困难,而且维护也很难:程序给客户后发现界面上某个标签
的字写错了,然后不得不把这样一个巨无霸重新编译,重新发给客户。
想到分割程序,用什么呢?COM/MTS?在小项目中太得不偿失了!用Dll?如果
是做非数据库程序还可以,如果做数据库程序就有麻烦了:每个Dll都会在自己独立
的对话中和数据库连接,造成资源的极大浪费,而且还有全局变量的问题。在当我
快绝望的时候看到了李维的一篇有关package的文章(关心package的朋友应该都看
过那篇文章),但那篇文章里写的不是很清楚,看了还有些不明白。大富翁上也有
很多朋友讨论,但都比较零碎,上一段时间结合网上查到的文章,还有自己一些摸索,
终于基本上搞清楚了package的一些用法,现在贴出来和大家交流。
package的使用和dll类似,有静态和动态调用两种方法。
我们用一个简单的数据库程序来说明,假设工程组成为:
ClassMgr.dpr--------------------工程文件
uGlobal.pas---------------------全局变量单元
frmDM.dfm(uDM.pas)--------------数据模块
frmMain.dfm(uMain.pas)----------主窗体
frmStudent.dfm(uStudent.pas)----学生档案窗体
frmScroe.dfm(uScore.pas)--------成绩输入窗体
frmQuery.dfm(uQuery.pas)--------成绩查询窗体
这里面的一些uses关系就不说了,大家应该都很清楚吧!
1、静态方法
现在的目标是把每个功能窗体放进一个包中,以后当需要修改相应的模块时只要发
布相应的包即可。为了达到这个目的,我们需要添加四个包:
basic.dpk
Contains: uDM.pas; uGlobal.pas
student.dpk
Contains: uStudent.pas
scroe.dpk
Contains: uScord.pas
query.dpk
Contains: uQuery.pas
因为所有的数据窗体都需要引用数据模块(uDM.pas)和全局变量(uGlobal.pas)单元,
所以我们把uGlobal.pas和uDM.pas放进basic.dpk中,至于为什么这么做我们等一下
再说。
在ClassMgr.dpr的Options中修改属性,使它Build with runtime packages,
package的列表为:
basic.bpl
student.bpl
scroe.bpl
query.bpl
另外,在student.dpk, scroe.dpk, query.dpk的Requires中添加basic.bpl;
basic.dpk的Requires中添加
vcl50.bpl
vcldb50.bpl
vclado50.bpl(假设用ADO连接)
接下来就是编译包和可执行文件,这里需要注意编译的顺序,正确的编译顺序应该为:
basic.bpl -> (student.dpk, scrod.dpk, query.dpk) -> Classmgr.dpr
编译完后看一下,classmgr.exe的体积是不是小很多了?差不多只用100k左右,因为
其实里面只包含了主窗体。每个bpl文件的大小也差不多100k左右(具体视代码规模)。
[red][b]这里需要额外说明的是:[/b][/red]
在主窗体frmMain中也要引用uDM, uGlobal等单元,但是因为已经Build with相应
的包了,所以Delphi只会编译相应的声明,而代码的实现部分在相应的包里。
2、动态方法
我们还是拿上面的例子
在这之前需要将每个窗体单元(主窗体除外)作一些修改:
即在每个窗体单元的Initialization部分注册相应的窗体类。
代码例子如下:
initialization
RegisterClass(TfrmStudent);
finalization
UnRegisterClass(TfrmStudent);
end.
然后在主窗体需要调用该窗体的地方改成下面的方式:
procedure TfrmMain.btStudentClick(Sender: TObject);
var
h: HMODULE;
frmStudent: TForm;
begin
try
h := LoadPackage('student.bpl');
frmStudent := TForm(TComponentClass(FindClass('TfrmStudent')).Create(Application));
frmStudent.ShowModal;
finally
frmStudent.Free;
UnLoadPackage(h);
end;
end;
在这里先载入包student.bpl, 然后取得我们在包里注册的TfrmStudent类,
因为FindClass返回的值是TPersistentClass类型,需要强制转换为TComponentClass类
后创建相应的实例。窗体释放后再释放student.bpl
在这种方式下就不需要在frmMain引用其他的单元了(全局变量单元除外)。需要补充
一点的是,在这种方式下,工程classmgr.dpr也需要Build with runtime package,
否则会在FindClass时无法取得相应的类指针。只是student.bpl等包不需要在package
列表中,只需添加basic.bpl即可。
现在说说为什么要将uDM.pas和uGlobal.pas加入到basic.bpl包中。
大家都知道win32模式下Dll不能直接和主程序共享全局变量,package的高明之处
就在于package和主程序能共享全局变量,所要作的工作只是把相应的全局变量放到某
个包中,然后引用该全局变量的包和主程序只要引用该包即可。因为在这个程序中
uGlobal.pas中存放的是全局变量,主程序和其他包都需要引用这些全局变量,故将它
加入basic.bpl中,uDM中的数据模块也是一个全局窗体变量,也需要加入basic.bpl中。
所以在其他包的Requires中都有basic.bpl
3、总结
================= 静态调用模式 =====================
一些关系:
classmgr.dpr
Build with runtime package: student.bpl, score.bpl, query.bpl, basic.bpl
uMain.pas中:
uses
uGlobal, uStudent, uScore, uQuery;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^不能删除
student.bpl
requires: basic.bpl
score.bpl
requires: basic.bpl
query.bpl
requires: basic.bpl
basic.bpl
requires: vcl50.bpl, vcldb50.bpl, vclado50.bpl
================= 动态调用模式 =====================
一些关系:
classmgr.dpr
Build with runtime package: basic.bpl
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^一定要
uMain.pas中:
uses
uGlogal, uStudent, uScore, uQuery;
^^^^^^^^^^^^^^^^^^^^^^^^^这几个单元不需要,但uGlobal仍需要
student.bpl
requires: basic.bpl
score.bpl
requires: basic.bpl
query.bpl
requires: basic.bpl
basic.bpl
requires: vcl50.bpl, vcldb50.bpl, vclado50.bpl
================= 两种模式的比较 =====================
理论上讲动态调用方式下会比较节省资源,因为相应的包只在需要的时候才
载入内存,但需要牺牲速度为代价。
但在实际使用中,项目窗体可能很多,包也可能有很多,频繁的载入包可能
会使开发者陷入一个比较混乱的状态,可能有的包载入了而没有释放掉,而且包
在什么时候释放也需要很好的控制,这样就不能达到节省资源的目的,相反却增
加了编程的复杂性和牺牲了速度。
另外,动态调用还有一个很明显的缺点是:调用时无法直接知道所取得的某
个类是否有某个方法或属性。
比如我们上面的例子中的frmStudent窗体有一个公有方法:GetStdInfo;
在动态调用时:
var
frmStudent: TForm;
begin
h := LoadPackage('student.bpl');
frmStudent := TForm(TComponentClass(FindClass('TfrmStudent')).Create(Application));
frmStudent.ShowModal;
这里我们就不能写入frmStudent.GetStdInfo这样的代码,因为事实上frmStudent
只是TForm类的一个实例,没有GetStdInfo这样的方法。在这方面动态package和COM有
点类似,但使用COM时编译器允许使用Variant方式来调用一个未知的方法,但Package
却不行。在静态调用时就不存在这种问题,因为代码的实现方式和不使用包时完全一样。
一点补充说明:不能在包之间交叉包含(Contains)某个单元,比如:在basic.bpl中已经
包含了uDM.pas单元,在student.bpl中就不能再包含uDM.pas这个单元,否则编译是不能
通过。
4、建议
我个人的意见是使用静态调用。但如果项目很大,package也就失去了她优势了,
到那个时候我想COM /MTS会是一个比较好的选择
程序体积太庞大!我写的上一个项目中可执行文件竟然达到4.3M!很可怕的体积!
这样不仅分发程序比较困难,而且维护也很难:程序给客户后发现界面上某个标签
的字写错了,然后不得不把这样一个巨无霸重新编译,重新发给客户。
想到分割程序,用什么呢?COM/MTS?在小项目中太得不偿失了!用Dll?如果
是做非数据库程序还可以,如果做数据库程序就有麻烦了:每个Dll都会在自己独立
的对话中和数据库连接,造成资源的极大浪费,而且还有全局变量的问题。在当我
快绝望的时候看到了李维的一篇有关package的文章(关心package的朋友应该都看
过那篇文章),但那篇文章里写的不是很清楚,看了还有些不明白。大富翁上也有
很多朋友讨论,但都比较零碎,上一段时间结合网上查到的文章,还有自己一些摸索,
终于基本上搞清楚了package的一些用法,现在贴出来和大家交流。
package的使用和dll类似,有静态和动态调用两种方法。
我们用一个简单的数据库程序来说明,假设工程组成为:
ClassMgr.dpr--------------------工程文件
uGlobal.pas---------------------全局变量单元
frmDM.dfm(uDM.pas)--------------数据模块
frmMain.dfm(uMain.pas)----------主窗体
frmStudent.dfm(uStudent.pas)----学生档案窗体
frmScroe.dfm(uScore.pas)--------成绩输入窗体
frmQuery.dfm(uQuery.pas)--------成绩查询窗体
这里面的一些uses关系就不说了,大家应该都很清楚吧!
1、静态方法
现在的目标是把每个功能窗体放进一个包中,以后当需要修改相应的模块时只要发
布相应的包即可。为了达到这个目的,我们需要添加四个包:
basic.dpk
Contains: uDM.pas; uGlobal.pas
student.dpk
Contains: uStudent.pas
scroe.dpk
Contains: uScord.pas
query.dpk
Contains: uQuery.pas
因为所有的数据窗体都需要引用数据模块(uDM.pas)和全局变量(uGlobal.pas)单元,
所以我们把uGlobal.pas和uDM.pas放进basic.dpk中,至于为什么这么做我们等一下
再说。
在ClassMgr.dpr的Options中修改属性,使它Build with runtime packages,
package的列表为:
basic.bpl
student.bpl
scroe.bpl
query.bpl
另外,在student.dpk, scroe.dpk, query.dpk的Requires中添加basic.bpl;
basic.dpk的Requires中添加
vcl50.bpl
vcldb50.bpl
vclado50.bpl(假设用ADO连接)
接下来就是编译包和可执行文件,这里需要注意编译的顺序,正确的编译顺序应该为:
basic.bpl -> (student.dpk, scrod.dpk, query.dpk) -> Classmgr.dpr
编译完后看一下,classmgr.exe的体积是不是小很多了?差不多只用100k左右,因为
其实里面只包含了主窗体。每个bpl文件的大小也差不多100k左右(具体视代码规模)。
[red][b]这里需要额外说明的是:[/b][/red]
在主窗体frmMain中也要引用uDM, uGlobal等单元,但是因为已经Build with相应
的包了,所以Delphi只会编译相应的声明,而代码的实现部分在相应的包里。
2、动态方法
我们还是拿上面的例子
在这之前需要将每个窗体单元(主窗体除外)作一些修改:
即在每个窗体单元的Initialization部分注册相应的窗体类。
代码例子如下:
initialization
RegisterClass(TfrmStudent);
finalization
UnRegisterClass(TfrmStudent);
end.
然后在主窗体需要调用该窗体的地方改成下面的方式:
procedure TfrmMain.btStudentClick(Sender: TObject);
var
h: HMODULE;
frmStudent: TForm;
begin
try
h := LoadPackage('student.bpl');
frmStudent := TForm(TComponentClass(FindClass('TfrmStudent')).Create(Application));
frmStudent.ShowModal;
finally
frmStudent.Free;
UnLoadPackage(h);
end;
end;
在这里先载入包student.bpl, 然后取得我们在包里注册的TfrmStudent类,
因为FindClass返回的值是TPersistentClass类型,需要强制转换为TComponentClass类
后创建相应的实例。窗体释放后再释放student.bpl
在这种方式下就不需要在frmMain引用其他的单元了(全局变量单元除外)。需要补充
一点的是,在这种方式下,工程classmgr.dpr也需要Build with runtime package,
否则会在FindClass时无法取得相应的类指针。只是student.bpl等包不需要在package
列表中,只需添加basic.bpl即可。
现在说说为什么要将uDM.pas和uGlobal.pas加入到basic.bpl包中。
大家都知道win32模式下Dll不能直接和主程序共享全局变量,package的高明之处
就在于package和主程序能共享全局变量,所要作的工作只是把相应的全局变量放到某
个包中,然后引用该全局变量的包和主程序只要引用该包即可。因为在这个程序中
uGlobal.pas中存放的是全局变量,主程序和其他包都需要引用这些全局变量,故将它
加入basic.bpl中,uDM中的数据模块也是一个全局窗体变量,也需要加入basic.bpl中。
所以在其他包的Requires中都有basic.bpl
3、总结
================= 静态调用模式 =====================
一些关系:
classmgr.dpr
Build with runtime package: student.bpl, score.bpl, query.bpl, basic.bpl
uMain.pas中:
uses
uGlobal, uStudent, uScore, uQuery;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^不能删除
student.bpl
requires: basic.bpl
score.bpl
requires: basic.bpl
query.bpl
requires: basic.bpl
basic.bpl
requires: vcl50.bpl, vcldb50.bpl, vclado50.bpl
================= 动态调用模式 =====================
一些关系:
classmgr.dpr
Build with runtime package: basic.bpl
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^一定要
uMain.pas中:
uses
uGlogal, uStudent, uScore, uQuery;
^^^^^^^^^^^^^^^^^^^^^^^^^这几个单元不需要,但uGlobal仍需要
student.bpl
requires: basic.bpl
score.bpl
requires: basic.bpl
query.bpl
requires: basic.bpl
basic.bpl
requires: vcl50.bpl, vcldb50.bpl, vclado50.bpl
================= 两种模式的比较 =====================
理论上讲动态调用方式下会比较节省资源,因为相应的包只在需要的时候才
载入内存,但需要牺牲速度为代价。
但在实际使用中,项目窗体可能很多,包也可能有很多,频繁的载入包可能
会使开发者陷入一个比较混乱的状态,可能有的包载入了而没有释放掉,而且包
在什么时候释放也需要很好的控制,这样就不能达到节省资源的目的,相反却增
加了编程的复杂性和牺牲了速度。
另外,动态调用还有一个很明显的缺点是:调用时无法直接知道所取得的某
个类是否有某个方法或属性。
比如我们上面的例子中的frmStudent窗体有一个公有方法:GetStdInfo;
在动态调用时:
var
frmStudent: TForm;
begin
h := LoadPackage('student.bpl');
frmStudent := TForm(TComponentClass(FindClass('TfrmStudent')).Create(Application));
frmStudent.ShowModal;
这里我们就不能写入frmStudent.GetStdInfo这样的代码,因为事实上frmStudent
只是TForm类的一个实例,没有GetStdInfo这样的方法。在这方面动态package和COM有
点类似,但使用COM时编译器允许使用Variant方式来调用一个未知的方法,但Package
却不行。在静态调用时就不存在这种问题,因为代码的实现方式和不使用包时完全一样。
一点补充说明:不能在包之间交叉包含(Contains)某个单元,比如:在basic.bpl中已经
包含了uDM.pas单元,在student.bpl中就不能再包含uDM.pas这个单元,否则编译是不能
通过。
4、建议
我个人的意见是使用静态调用。但如果项目很大,package也就失去了她优势了,
到那个时候我想COM /MTS会是一个比较好的选择
package(转)
Red Hat Package Manager(RPM的介绍和应用)
Red Hat Package Manager--rpm
Steel sector gets booster package
a clip package massage chengdu
struts2的配置元素package
Erlang package in Ubuntu 9.10
Xinjiang support package hailed by local residents
Ireland, EU agree on financial rescue package
FreeBSD installation and package tools, past, present and future
UGG Cove Boots, Full Package of Comfort, Warmth and Cuteness
Document a package using Javadoc - Real's Java How-to
Struts2---- package分类And空间命名解决action 重名问题
China's stimulus package benefits China and world
Sudan gov't rejects new U.S incentives package
详解java源文件涉及到的package问题 问题 涉及 详解 路径 full_dir b...
struts2配置中Action的name 和package的name和namespace是什么作用
struts2配置中Action的name 和package的name和namespace是什么作用
独特散热技术:Heat Sink package,台湾流明斯重拳出击,一款高量低光衰小功率...
!(转)
(转)
(转)
ARM是什么?(转)
为什么(转)