了解BPL和DLL的关系将有助于我们更好地理解DELPHI在构件制作、运用和动态、静态编译的工作方式。对初学DELPHI但仍对DELPHI开发不甚清晰的朋友有一定帮助。
第一部分:有关包的介绍
一般我们编写编译一个DELPHI应用程序时,会产生一个EXE文件,也就是一个独立的WINDOWS应用程序。很重要的一点:区别于Visual Basic,DELPHI产生的是预先包裹的应用程序是不需要大量的运行库(DLL‘s)。
假设:打开Delphi默认的工程(只有一个空白form),F9她将编译生成一个大约295 KB (Delphi 5)的可执行文件。然后打开Project | Options,把‘Build with runtime packages’选上再编译一下,EXE文件大小就只有15 KB左右了。
我们编译一个DELPHI应用程序时默认地没有选择‘Build with runtime packages‘,编译器将把程序运行所需要的代码直接写入你的EXE文件中,因此产生的程序是一个相对独立的程序,并不需要任何附属的支持文件(例如动态运行库文件DLL),这也就知道了为什么DELPHI产生的应用程序为什么都那么大。
要建立尽可能小的DELPHI程序,方法之一就要充分发挥Borland package libraries的作用,简称BPL。
先说什么是包?
简而言之,一个包就是一个在DELPHI的IDE环境中被DELPHI应用程序共享的特殊的动态链接库。包允许我们通过多级应用将我们的程序的一部分当做一个分离的模块供其他应用程序来共享。
包大致可分为运行期包(Run-time packages)和设计期包(Design-time packages):
运行期包-当运行程序时提供VCL和库函数的支持,操作上很类似标准的动态链接库。
设计期包-用来在DELPHI的IDE环境安装控件和为控件建立特殊的属性编辑器。设计期包允许包含控件、属性和控件编辑器等等,在IDE环境中,这类包是程序设计所必需的,也仅仅是DELPHI使用,并不和开发的应用程序一起分发。
知道这些包的运用,我们也就知道了运行期包是如何做处理和它们对DELPHI程序员有什么帮助了。有一点必须说明:想很好地运用包并不要求你先成为一个成熟的控件编写者。DELPHI编程初学者也可以和应该尝试去接触包的概念,这将有利于你更好地理解包和DELPHI的工作关系。
第二部分:适时运用包裹和DLL
一般都认为加入WINDOWS操作系统中的动态运行库是一种最有用最高效的应用。在WINDOWS系统中,很多应用程序同时运行可能会引起了内存方面的问题,很多程序执行相似的操作任务,但各自又由不同的代码来控制并完成任务,动态运行库的作用就是将你的执行程序中的这些代码放到一个系统共享环境下的DLL中去。可能最为直观的动态链接库例子就是WINDOWS操作系统自己和它本身所带的API了。
动态链接库通常都是用来集合过程(procedure)和函数(function)以供程序调用。当然我们在编写动态链接库的同时,也可以把一个DELPHI FORM放到一个DLL中去(例如一个AboutBox FORM),此外我们也可以在DLL中存储程序所需要的资源(resources)。更多关于DELPHI如何操作使用动态链接库,请参考相关书籍,不再赘述。
在比较DLLs 和BPLs之前,我们先要知道可执行文件的2种代码链接的方式:静态链接和动态链接。
静态链接就是当一个DELPHI工程被编译的时候,工程所需要的所有代码将被直接链接入你的程序执行文件。结果就是执行文件将包含程序所需要使用到的所有单元(units),你也许会说这样代码有点冗长,因为在通常默认情况下,一个FORM单元的uses子句列举了至少5个基本单元(如:Windows, Messages, SysUtils,...),尽管如此,DELPHI还是能够智能地自动链接单元中真正要用到的代码到工程代码中,从而尽可能地减少了执行文件的大小。使用静态链接,我们的应用程序就是一个相对独立的程序,不需要任何额外的支持文件或动态链接库(暂时不考虑BDE和ActiveX构件)。DELPHI中默认使用的就是静态链接方式。
动态链接就是应用程序将和标准的动态链接库(DLLs)一起运行。动态链接方式不需要将代码直接建立到每个应用程序中去,单独为多个应用程序提供多线程的库函数支持,任何程序运行期间才需用到的包才将被加载,更值得一提的是:程序在动态方式需要调用的包是自动加载的,因此你不需要专门写加载包的代码。
方法:简单地选中在Project | Options 对话框中‘Build with runtime packages‘复选框后,再次编译你的程序,你的程序代码将自动链接到动态运行包,而不是将引用单元都静态链接入你的工程执行文件。
是选择BPL 还是 DLL?区别又在哪里?
你可能很奇怪为什么要选择使用运行期包,而不是DLL,或者还有其他什么方法。
相对于DLL而言,包裹的概念是DELPHI开发中所特有的,就是说其他语言编写的应用程序不能引用DELPHI建立的包裹。即使包是一种被DELPHI编写的应用程序所使用的动态链接库,它也同时提供给了DELPHI程序员更多的库函数支持。
通常我们在DELPHI中建立动态链接库(DLLs)是用来存储不同环境下应用程序所需要使用到的过程和函数,而包不仅能够包含代码单元(untits)、构件和FORMs,还能包含DELPHI中的类(classes)-这就使我们能够在其中引用对象向导编码(object oriented code)。在包裹中,我们可以保存完整的通用DELPHI构件,而动态运行库(DLL‘s)对此则无能为力了。
此外,在缩减程序代码上,DLLs和BPLs扮演着同样重要的角色,其主要原因就是在使用包裹或动态链接库技术后,都直接地减少了程序的文件大小。当然,还要说明的是:执行程序需要加载的DLLs或BPLs也可能会是很庞大的。例如如果需要分发你的包裹文件(主要是VCL包,vcl50.BPL)至少有2MB左右。
尽管如此,如果你是要分发共享同个包的多个应用程序,你就可以省很多事了。当用户方系统中已经存在程序运行需要的部分文件(如:标准的DELPHI BPLs)后,就只需要下载程序的最小执行文件了。如果你的程序工程主要是通过INTERNET等方式分发和开展,那效率显然有很大的提高。
同时,包的应用也节省系统内存,,因为动态链接的结果就是:只有一个VCL被读入内存供所有使用运行期包的DLEPHI应用程序使用。
包裹的版本问题
当你想升级你的动态链接库时(改变其中一些执行函数),你可以简单地编译产生新的程序文件,并上载新版本文件,所有正在使用该动态链接库的应用程序仍将工作(除非你已经将存在的旧版本程序去除)。
换个角度来讲,在升级新包裹文件的同时,不要忘记升级程序的执行文件。正如你所了解的,包裹文件就是一个单元文件(units)的集合,所有编译过的单元文件(DCU)都含有版本信息,因此,除非我们有单元文件的源码(source),否则我们不能在DELPHI4或5中使用编译过的单元,所以一旦我们改变了单元文件中接口部分uses子句中列举出的任一单元文件,该文件就需要重新编译。编译器将检查DCU文件的版本信息,并决定单元是否需要重新编译。因此我们不能在DELPHI5编译的应用程序中使用在DELPHI6下编译的包,任何为你的应用程序服务的包和你的应用程序必须在相同环境下编译。
因此,当给包裹命名的时要保留包裹名中包含有DELPHI的版本信息(如‘AboutDP50‘,其中50就代表Delphi 5)。这可以有效防止文件版本的冲突问题,也可以避免很多不必要的麻烦,包使用者可以更清楚包的版本和包裹适用于哪个DELPHI编译器。
如果你要分发运行期或设计期包给其他DELPHI程序员,建议同时提供了.DCP(含有包的头信息和各个单元文件)和.BPL文件,还有包中所包含的所有单元文件的.DCU文件。
第三部分:建立和使用运行期包
建立一个包裹
建立一个包很简单,但在建包之前要做一些准备工作。首先,你需要知道你准备建立哪种类型的包文件:运行期包还是设计期包,或两者都是;其次是,建立、调试,反复地测试你想放置到包中去的单元文件;最后,为建立的包裹文件命名,还有就是选择一个合适的地方来存放文件。
建立一个新的运行期包,按照以下步骤:
启动DELPHI,并选择File | Close All关闭默认的工程。