第五章          包

我们在日常生活中会用到各式各样的包,钱包、公文包、背包、书包……,包里面都装有经常要用的东西。一旦你那天要出门远行,带上该带的包就可以了。不用再考虑包里面具体的东西,尽管包里有的东西用得着而有的东西用不着。有了这些包,你就可以一身轻松地旅游。当然,打包打得不好或者包太多也会成为累赘。

同样,在DELPHI中,你可以将常用的程序和数据放到包里,让这些包伴随你的程序发布和运行。这时候,程序可以变得很短小而轻盈,轻松一身。特别是在包可以被多个应用程序模块共同使用的情况下,可以让应用程序的分发与管理变得更加方便自如。

第一节 包是什么

包是一种特殊的动态连接库,它是以.BPL(Borland Package Library)为扩展名的可执行模块。这种动态连接库被应用于DELPHI应用程序或DELPHI的IDE。在DELPHI中,有仅用于运行时的包(Runtime Only Packages)、仅用于设计时的包(Designtime Only Packages)以及可同时用于运行时和设计时的包(Runtime and Designtime Packages)。

运行时包为应用程序提供特定的功能,是应用程序所依赖的运行库。而设计时包用于将元件安装到DELPHI的IDE环境中,建立诸如仅在IDE中使用的属性编辑器等辅助设计元素,以及扩展IDE功能的设计专家和向导程序等。一个包可以同时具有设计时和运行时的功能,而设计时包也一般都会调用运行包。

与其他运行库一样,包中包含有应用程序可共享的代码和数据。在DELPHI6中,最常用的包可能应该算是VCL60.BPL。按包模式编译的应用程序一般都会用到VCL60.BPL,除非你的应用程序不使用DELPHI的元件库。按包模式编译时,引用某个包的可执行模块,如EXE、DLL或其BPL模块,只会包含对该包中的数据和代码的引用,而不会包含这些代码体和数据体。

想要在DELPHI中将程序编译成使用包的形式是非常简单的。只要打开Project Options对话框Packages页面的Build with runtime packages选项,并在其下面的编辑框中输入想要是用得包,然后编译程序即可。

 

用包编译模式编译的执行文件尺寸往往比正常编译的执行文件小一个数量级。这是因为,正常编译出来的执行文件中,本身就包含了程序所用到的DELPHI控件、过程和函数等一大堆的数据和代码。正常编译的执行文件通常可以独立运行,不需要其它支持。而用包编译模式编译出来的执行文件中,是不包含DELPHI控件、过程和函数等数据和代码的。不过它的运行是需要包文件支持的,即需要*.BPL文件(Borland Package Library)的支持。

编写使用包的程序与编写不使用包的程序没有任何的不同。都是use相关的单元,然后使用该单元中的数据、对象、过程、函数和方法。最后,只需要要在编译时决定是否使用包模式,这就是DELPHI的方便性和简洁性。

在DELPHI中,包也是IDE赖以进行可视化程序设计的基本核心。几乎所有的DELPHI元件都存在于包之中。在DELPHI的设计开发环境中使用的包是设计包,元件面板上的各种元件就是它提供的。

一个包可以是专门用于可视化设计 环境中(设计包),也可以是专门用于运行环境中(运行包),或者两种环境下都可以用的(设计和运行包)。什么东西都可以放到包里,常量、变量、过程、函数、类、元件……,真的可以说是包罗万象。

你也可以创建你自己的包,并将常用的程序单元放到包中去。


第二节 包的原理

我们知道包编译后生成的BPL文件实际上就是一个DLL文件。但是,我们在程序中使用包中的过程或函数时,既不需要在包中引出(exports)这些过程和函数,也不需要在引用的程序将这些过程和函数声明为外部的(external)。而使用DLL中的过程或函数则必须这样做。虽然,与DLL一样,这些过程和代码在进程空间中都是共享的,但包中的过程或函数使用起来更方便直观。更为惊奇的是,包中声明的全局变量是可以直接访问的,而且在包中定义的一个全局变量,在整个程序空间中有且只有一个该变量。既不会象DLL中的变量那样不可以直接访问,又不象非包编译的各个可执行模块那样各自有一份该变量的副本。

这是为什么呢?

原来,这是DELPHI施的魔法。

第三节 调试包

以前,我曾读到一本讲开发DELPHI元件的好书,写的很精彩,让我受益颇多。不过其中谈到:由于没有办法在源程序基础上调试包中的元件,所以开发元件一定要特别认真仔细的写好元件的代码。的确,用DELPHI单独打开一个*.DPK文件,DELPHI的Run(F9)、Step Over(F8)、Track Into(F7)等Debug命令全部无效。自己再想想,DELPHI的IDE又与设计时包文件有着非常密切的关系。在加载了包的IDE运行环境中要调试包本身,似乎有点像逻辑学上的自反悖论。后来,我就一直相信包是不能在源程序基础上调试的。

然而,包是可以直接在DELPHI的IDE环境内调试原代码的!不但可以调试运行时刻的包(Runtime Package),而且还可以调试设计时刻包(Designtime Package)!

在一次偶然中,我硬是强行将我的包加入到DELPHI的项目组(Project Group)中。这时,我惊奇地发现运行程序的工具按钮是有效的。当时,我真不敢相信自己的眼睛!特别是有那本权威书籍在我脑海中形成的根深蒂固的印象。

随后的实践证明,只要包文件处在一个项目组中,就可以象调试DLL项目一样调试它。只需要在运行参数(Run Paramenters)中设置相应的主应用程序(Host Application),即可开始调试。当然,主应用程序一定要是按包编译(Build with runtime packages)的。

为什么会这样呢?

我想,DELPHI的一个项目文件编译之后最终都会形成一个可执行模块,要么是EXE文件,要么是DLL文件。其实,包编译后的BPL文件也是一个DLL文件,一个可执行模块。因此,DPK文件也相当于是一个项目文件。只是,用DELPHI打开DPK文件时没有被当作项目打开,不知何故。而在项目组中,DELPHI把每一个加入的项都当作项目来激活。要知道,一个典型的应用系统常常是由EXE、DLL和BPL共同组成。因此,包作为项目加入到项目组中也是合情合理的,DELPHI也因该是这样处理。

要调试一个单独的包,可以这样操作。首先,关闭DELPHI打开的所有项目,即用File\Close All菜单命令。然后,用View\Project Manager菜单命令打开项目管理器。接着,在项目管理器中按鼠标右键,选择Add Existing Project命令。在弹出的打开文件窗口中,应该选择打开*.DPK的文件。找到你要调试的包文件之后,即可将包文件加入当前项目组中。这时,DELPHI的Run(F9)、Step Over(F8)、Track Into(F7)等Debug命令都是有效的。最后,只需要设置主应用程序(Host Application),即可开始调试包。

如果要调试Designtime Package,或者要调试你开发的元件在IDE环境中的错误,该怎么办呢?

很简单,只需要将Host Application设置为Delphi32.exe即可。当然Delphi32.exe的具体位置要看你安装DELPHI的具体目录,典型的是:C:\Program Files\Borland\Delphi5\Bin\目录。这样,你就可随心所欲地调试包在IDE环境下的行为了。不过,不要忘记一定要首先将你的包安装到IDE环境中,否则Delphi32.exe启动时并不会加载未安装的包。


作者:菩提树下的杨过