Chapter 9
Working with packages and components

A package is a special dynamic-link library used by Delphi applications, the IDE, or both. Runtime packages provide functionality when a user runs an application. Design-time packages are used to install components in the IDE and to create special property editors for custom components. A single package can function at both design time and runtime, and design-time packages frequently work by calling runtime packages. To distinguish them from other DLLs, package libraries are stored in files that end with the .BPL (Borland package library) extension.

Like other runtime libraries, packages contain code that can be shared among applications. For example, the most frequently used Delphi components reside in a package called VCL50. Each time you create an application, you can specify that it uses VCL50. When you compile an application created this way, the application's executable image contains only the code and data unique to it; the common code is in VCL50.BPL. A computer with several package-enabled applications installed on it needs only a single copy of VCL50.BPL, which is shared by all the applications and the IDE itself.

Delphi ships with several precompiled runtime packages, including VCL50, that encapsulate VCL components. Delphi also uses design-time packages to manipulate components in the IDE.

You can build applications with or without packages. However, if you want to add custom components to the IDE, you must install them as design-time packages.

You can create your own runtime packages to share among applications. If you write Delphi components, you can compile your components into design-time packages before installing them.

Why use packages?

Design-time packages simplify the tasks of distributing and installing custom components. Runtime packages, which are optional, offer several advantages over conventional programming. By compiling reused code into a runtime library, you can share it among applications. For example, all of your applications--including Delphi itself--can access standard components through packages. Since the applications don't have separate copies of the component library bound into their executables, the executables are much smaller--saving both system resources and hard disk storage. Moreover, packages allow faster compilation because only code unique to the application is compiled with each build.

Packages and standard DLLs

Create a package when you want to make a custom component that's available through the IDE. Create a standard DLL when you want to build a library that can be called from any Windows application, regardless of the development tool used to build the application.

The following table lists the file types associated with packages.

Table 9.1   Compiled package files

File extension

Contents

.DPK

The source file listing the units contained in the package.

DCP

A binary image containing a package header and the concatenation of all DCU files in the package, including all symbol information required by the compiler. A single DCP file is created for each package. The base name for the DCP is the base name of the DPK source file. You must have a .DCP file to build an application with packages.

DCU

A binary image for a unit file contained in a package. One DCU is created, when necessary, for each unit file.

BPL

The runtime package. This file is a Windows DLL with special Delphi-specific features. The base name for the BPL is the base name of the DPK source file.

Note: Packages share their global data with other modules in an application.

For more information about DLLs and packages, see the Object Pascal Language Guide.

Runtime packages

Runtime packages are deployed with Delphi applications. They provide functionality when a user runs the application.

To run an application that uses packages, a computer must have both the application's .EXE file and all the packages (.BPL files) that the application uses. The .BPL files must be on the system path for an application to use them. When you deploy an application, you must make sure that users have correct versions of any required .BPLs.

Using packages in an application

To use packages in an application,

  1. Load or create a project in the IDE.
  2. Choose Project|Options.
  3. Choose the Packages tab.
  4. Select the "Build with Runtime Packages" check box, and enter one or more package names in the edit box underneath. (Runtime packages associated with installed design-time packages are already listed in the edit box.) To add a package to an existing list, click the Add button and enter the name of the new package in the Add Runtime Package dialog. To browse from a list of available packages, click the Add button, then click the Browse button next to the Package Name edit box in the Add Runtime Package dialog.

    If you edit the Search Path edit box in the Add Runtime Package dialog, you will be changing Delphi's global Library Path.

    You do not need to include file extensions with package names. If you type directly into the Runtime Packages edit box, be sure to separate multiple names with semicolons. For example:

    VCL50;VCLDB50;VCLDBX50
    

Packages listed in the Runtime Packages edit box are automatically linked to your application when you compile. Duplicate package names are ignored, and if the edit box is empty the application is compiled without packages.

Runtime packages are selected for the current project only. To make the current choices into automatic defaults for new projects, select the "Defaults" check box at the bottom of the dialog.

Note: When you create an application with packages, you still need to include the names of the original Delphi units in the uses clause of your source files. For example, the source file for your main form might begin like this:

unit MainForm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

Each of the units referenced in this example is contained in the VCL50 package. Nonetheless, you must keep these references in the uses clause, even if you use VCL50 in your application, or you will get compiler errors. In generated source files, Delphi adds these units to the uses clause automatically.

Dynamically loading packages

To load a package at runtime, call the LoadPackage function. For example, the following code could be executed when a file is chosen in a file-selection dialog.

with OpenDialog1 do
  if Execute then
    with PackageList.Items do
      AddObject(FileName, Pointer(LoadPackage(Filename)));

To unload a package dynamically, call UnloadPackage. Be careful to destroy any instances of classes defined in the package and to unregister classes that were registered by it.

Deciding which runtime packages to use

Delphi ships with several precompiled runtime packages, including VCL50, which supply basic language and component support.

The VCL50 package contains the most commonly used components, system functions, and Windows interface elements. It does not include database or Windows 3.1 components, which are available in separate packages. The following table lists some runtime packages shipped with Delphi and the units they contain.

Table 9.2   Runtime packages

Package

Units

VCL50.BPL

Ax, Buttons, Classes, Clipbrd, Comctrls, Commctrl, Commdlg, Comobj, Comstrs, Consts, Controls, Ddeml, Dialogs, Dlgs, Dsgnintf, Dsgnwnds, Editintf, Exptintf, Extctrls, Extdlgs, Fileintf, Forms, Graphics, Grids, Imm, IniFiles, Isapi, Isapi2, Istreams, Libhelp, Libintf, Lzexpand, Mapi, Mask, Math, Menu, Messages, Mmsystem, Nsapi, Ole2I, Oleconst, Olectnrs, Olectrls, Oledlg, Penwin, Printers, Proxies, Registry, Regstr, Richedit, Shellapi, Shlobj, Stdctrls, Stdvcl, Sysutils, Tlhelp32, Toolintf, Toolwin, Typinfo, Vclcom, Virtintf, Windows, Wininet, Winsock, Winspool, Winsvc

VCLX50.BPL

Checklst, Colorgrd, Ddeman, Filectrl, Mplayer, Outline, Tabnotbk, Tabs

VCLDB50.BPL

Bde, Bdeconst, Bdeprov, Db, Dbcgrids, Dbclient, Dbcommon, Dbconsts, Dbctrls, Dbgrids, Dbinpreq, Dblogdlg, Dbpwdlg, Dbtables, Dsintf, Provider, SMintf

VCLDBX50.BPL

Dblookup, Report

DSS50.BPL

Mxarrays, Mxbutton, Mxcommon, Mxconsts, Mxdb, Mxdcube, Mxdssqry, Mxgraph, Mxgrid, Mxpivsrc, Mxqedcom, Mxqparse, Mxqryedt, Mxstore, Mxtables, Mxqvb

QRPT50.BPL

Qr2const, Qrabout, Qralias, Qrctrls, Qrdatasu, Qrexpbld, Qrextra, Qrprev, Qrprgres, Qrprntr, Qrqred32, Quickrpt

TEE50.BPL

Arrowcha, Bubblech, Chart, Ganttch, Series, Teeconst, Teefunci, Teengine, Teeprocs, Teeshape

TEEDB50.BPL

Dbchart, Qrtee

TEEUI50.BPL

Areaedit, Arrowedi, Axisincr, Axmaxmin, Baredit, Brushdlg, Bubbledi, Custedit, Dbeditch, Editchar, Flineedi, Ganttedi, Ieditcha, Pendlg, Pieedit, Shapeedi, Teeabout, Teegally, Teelisb, Teeprevi, Teexport

VCLSMP50.BPL

Sampreg, Smpconst

To create a client/server database application that uses packages, you need at least two runtime packages: VCL50 and VCLDB50. If you want to use Outline components in your application, you also need VCLX50. To use these packages, choose Project|Options, select the Packages tab, and enter the following list in the Runtime Packages edit box.

VCL50;VCLDB50;VCLX50

Actually, you don't have to include VCL50, because VCL50 is referenced in the Requires clause of VCLDB50. (See "The Requires clause".) Your application will compile just the same whether or not VCL50 is included in the Runtime Packages edit box.

Custom packages

A custom package is either a BPL you code and compile yourself, or a precompiled package from a third-party vendor. To use a custom runtime package with an application, choose Project|Options and add the name of the package to the Runtime Packages edit box on the Packages page. For example, suppose you have a statistical package called STATS.BPL. To use it in an application, the line you enter in the Runtime Packages edit box might look like this:

VCL50;VCLDB50;STATS

If you create your own packages, you can add them to the list as needed.

Design-time packages

Design-time packages are used to install components on the IDE's Component palette and to create special property editors for custom components.

Delphi ships with the following design-time component packages preinstalled in the IDE.

Table 9.3   Design-time packages

Package

Component palette pages

DCLSTD50.BPL

Standard, Additional, System, Win32, Dialogs

DCLTEE50.BPL

Additional (TChart component)

DCLDB50.BPL

Data Access, Data Controls

DCLMID50.BPL

Data Access (MIDAS)

DCL31W50.BPL

Win 3.1

DCLNET50.BPL, NMFAST50.BPL

Internet

DCLSMP50.BPL

Samples

DCLOCX50.BPL

ActiveX

DCLQRT50.BPL

QReport

DCLDSS50.BPL

Decision Cube

IBSMP50.BPL

Samples (IBEventAlerter component)

DCLINT50.BPL

(International Tools--Resource DLL wizard)

RCEXPERT.BPL

(Resource Expert)

DBWEBXPRT.BPL

(Web Wizard)

These design-time packages work by calling runtime packages, which they reference in their Requires clauses. (See "The Requires clause".) For example, DCLSTD50 references VCL50. DCLSTD50 itself contains additional functionality that makes most of the standard components available on the Component palette.

In addition to preinstalled packages, you can install your own component packages, or component packages from third-party developers, in the IDE. The DCLUSR50 design-time package is provided as a default container for new components.

Installing component packages

In Delphi all components are installed in the IDE as packages. If you've written your own components, create and compile a package that contains them. (See "Creating and editing packages".) Your component source code must follow the model described in "Creating custom components".

To install or uninstall your own components, or components from a third-party vendor, follow these steps:

  1. If you are installing a new package, copy or move the package files to a local directory. If the package is shipped with .BPL, .DCP, and .DCU files, be sure to copy all of them. (For information about these files, see "Package files created by a successful compilation".)

    The directory where you store the .DCP file--and the .DCU files, if they are included with the distribution--must be in the Delphi Library Path.

    If the package is shipped as a .DPC (package collection) file, only the one file need be copied; the .DPC file contains the other files. (For more information about package collection files, see "Package collection files".)

  2. Choose Component|Install Packages from the IDE menu, or choose Project|Options and click the Packages tab.
  3. A list of available packages appears under "Design packages".

  4. Click OK.

The components in the package are installed on the Component palette pages specified in the components' RegisterComponents procedure, with the names they were assigned in the same procedure.

New projects are created with all available packages installed, unless you change the default settings. To make the current installation choices into the automatic default for new projects, check the Default check box at the bottom of the dialog box.

To remove components from the Component palette without uninstalling a package, select Component|Configure Palette, or select Tools|Environment Options and click the Palette tab. The Palette options tab lists each installed component along with the name of the Component palette page where it appears. Selecting any component and clicking Hide removes the component from the palette.

Creating and editing packages

Creating a package involves specifying

Package source files, which end with the .DPK extension, are generated by the Package editor.

Creating a package

To create a package, follow the procedure below. Refer to "Understanding the structure of a package" for more information about the steps outlined here.

  1. Choose File|New, select the Package icon, and click OK.
  2. The generated package is displayed in the Package editor.
  3. The Package editor shows a Requires node and a Contains node for the new package.
  4. To add a unit to the contains clause, click the Add to package speed button. In the Add unit page, type a .PAS file name in the Unit file name edit box, or click Browse to browse for the file, and then click OK. The unit you've selected appears under the Contains node in the Package editor. You can add additional units by repeating this step.
  5. To add a package to the requires clause, click the Add to package speed button. In the Requires page, type a .DCP file name in the Package name edit box, or click Browse to browse for the file, and then click OK. The package you've selected appears under the Requires node in the Package editor. You can add additional packages by repeating this step.
  6. Click the Options speed button, and decide what kind of package you want to build.

  7. In the Package editor, click the Compile package speed button to compile your package.

Editing an existing package

There are several ways to open an existing package for editing.

To edit a package's description or set usage options, click the Options speed button in the Package editor and select the Description tab.

The Project Options dialog has a Default check box in the lower left corner. If you click OK when this box is checked, the options you've chosen are saved as default settings for new projects. To restore the original defaults, delete or rename the DEFPROJ.DOF file.

Editing package source files manually

Package source files, like project files, are generated by Delphi from information you supply. Like project files, they can also be edited manually. A package source file should be saved with the .DPK (Delphi package) extension to avoid confusion with other files containing Object Pascal source code.

To open a package source file in the Code editor,

  1. Open the package in the Package editor.
  2. Right-click in the Package editor and select View Source.

    For example, the following code declares the VCLDB50 package.

    package VCLDB50;
      requires VCL50;
      contains Db, Dbcgrids, Dbctrls, Dbgrids, Dbinpreq, Dblogdlg, Dbpwdlg, Dbtables, 
    mycomponent in 'C:\components\mycomponent.pas';
    end.
    

Understanding the structure of a package

Naming packages

Package names must be unique within a project. If you name a package STATS, the Package editor generates a source file for it called STATS.DPK; the compiler generates an executable and a binary image called STATS.BPL and STATS.DCP, respectively. Use STATS to refer to the package in the requires clause of another package, or when using the package in an application.

The Requires clause

The requires clause specifies other, external packages that are used by the current package. An external package included in the requires clause is automatically linked at compile time into any application that uses both the current package and one of the units contained in the external package.

If the unit files contained in your package make references to other packaged units, the other packages should appear in your package's requires clause or you should add them. If the other packages are omitted from the requires clause, the compiler will import them into your package 'implicitly contained units'.

Note: Most packages that you create will require VCL50. Any package that depends on VCL units (including SysUtils) must list VCL50, or another package that requires VCL50, in its requires clause.

Avoiding circular package references

Packages cannot contain circular references in their requires clause. This means that

Handling duplicate package references

Duplicate references in a package's requires clause--or in the Runtime Packages edit box--are ignored by the compiler. For programming clarity and readability, however, you should catch and remove duplicate package references.

The Contains clause

The contains clause identifies the unit files to be bound into the package. If you are writing your own package, put your source code in PAS files and include them in the contains clause.

Avoiding redundant source code uses

A package cannot appear in the contains clause of another package.

All units included directly in a package's contains clause, or included indirectly in any of those units, are bound into the package at compile time.

A unit cannot be contained (directly or indirectly) in more than one package used by the same application, including the Delphi IDE. This means that if you create a package that contains one of the units in VCL50, you won't be able to install your package in the IDE. To use an already-packaged unit file in another package, put the first package in the second package's requires clause.

Compiling packages

You can compile a package from the IDE or from the command line. To recompile a package by itself from the IDE,

  1. Choose File|Open.
  2. Select Delphi Package Source (*.DPK) from the Files Of Type drop-down list.
  3. Select a .DPK file in the dialog.
  4. When the Package editor opens, click the Compile speed button.

You can insert compiler directives into your package source code. For more information, see "Package-specific compiler directives", below.

If you compile from the command line, several package-specific switches are available. For more information, see "Using the command-line compiler and linker".

Package-specific compiler directives

The following table lists package-specific compiler directives that you can insert into your source code.

Table 9.4   Package-specific compiler directives

Directive

Purpose

{$IMPLICITBUILD OFF}

Prevents a package from being implicitly recompiled later. Use in .DPK files when compiling packages that provide low-level functionality, that change infrequently between builds, or whose source code will not be distributed.

{$G-} or {IMPORTEDDATA OFF}

Disables creation of imported data references. This directive increases memory-access efficiency, but prevents the unit where it occurs from referencing variables in other packages.

{$WEAKPACKAGEUNIT ON}

Packages unit "weakly." See "Weak packaging" below.

{$DENYPACKAGEUNIT ON}

Prevents unit from being placed in a package.

{$DESIGNONLY ON}

Compiles the package for installation in the IDE. (Put in .DPK file.)

{$RUNONLY ON}

Compiles the package as runtime only. (Put in .DPK file.)

Note: Including {$DENYPACKAGEUNIT ON} in your source code prevents the unit file from being packaged. Including {$G-} or {IMPORTEDDATA OFF} may prevent a package from being used in the same application with other packages. Packages compiled with the {$DESIGNONLY ON} directive should not ordinarily be used in applications, since they contain extra code required by the IDE. Other compiler directives may be included, if appropriate, in package source code. See Compiler directives in the online help for information on compiler directives not discussed here.

Weak packaging

The $WEAKPACKAGEUNIT directive affects the way a .DCU file is stored in a package's .DCP and .BPL files. (For information about files generated by the compiler, see "Package files created by a successful compilation".) If {$WEAKPACKAGEUNIT ON} appears in a unit file, the compiler omits the unit from BPLs when possible, and creates a non-packaged local copy of the unit when it is required by another application or package. A unit compiled with this directive is said to be "weakly packaged."

For example, suppose you've created a package called PACK that contains only one unit, UNIT1. Suppose UNIT1 does not use any further units, but it makes calls to RARE.DLL. If you put {$WEAKPACKAGEUNIT ON} in UNIT1.PAS when you compile your package, UNIT1 will not be included in PACK.BPL; you will not have to distribute copies of RARE.DLL with PACK. However, UNIT1 will still be included in PACK.DCP. If UNIT1 is referenced by another package or application that uses PACK, it will be copied from PACK.DCP and compiled directly into the project.

Now suppose you add a second unit, UNIT2, to PACK. Suppose that UNIT2 uses UNIT1. This time, even if you compile PACK with {$WEAKPACKAGEUNIT ON} in UNIT1.PAS, the compiler will include UNIT1 in PACK.BPL. But other packages or applications that reference UNIT1 will use the (non-packaged) copy taken from PACK.DCP.

Note: Unit files containing the {$WEAKPACKAGEUNIT ON} directive must not have global variables, initialization sections, or finalization sections.

The $WEAKPACKAGEUNIT directive is an advanced feature intended for developers who distribute their BPLs to other Delphi programmers. It can help you to avoid distribution of infrequently used DLLs, and to eliminate conflicts among packages that may depend on the same external library.

For example, Delphi's PenWin unit references PENWIN.DLL. Most projects don't use PenWin, and most computers don't have PENWIN.DLL installed on them. For this reason, the PenWin unit is weakly packaged in VCL50. When you compile a project that uses PenWin and the VCL50 package, PenWin is copied from VCL50.DCP and bound directly into your project; the resulting executable is statically linked to PENWIN.DLL.

If PenWin were not weakly packaged, two problems would arise. First, VCL50 itself would be statically linked to PENWIN.DLL, and so you could not load it on any computer which didn't have PENWIN.DLL installed. Second, if you tried to create a package that contained PenWin, a compiler error would result because the PenWin unit would be contained in both VCL50 and your package. Thus, without weak packaging, PenWin could not be included in standard distributions of VCL50.

Using the command-line compiler and linker

When you compile from the command line, you can use the package-specific switches listed in the following table.

Table 9.5   Package-specific command-line compiler switches

Switch

Purpose

-$G-

Disables creation of imported data references. Using this switch increases memory-access efficiency, but prevents packages compiled with it from referencing variables in other packages.

-LEpath

Specifies the directory where the package BPL file will be placed.

-LNpath

Specifies the directory where the package DCP file will be placed.

-LUpackage

Use packages.

-Z

Prevents a package from being implicitly recompiled later. Use when compiling packages that provide low-level functionality, that change infrequently between builds, or whose source code will not be distributed.

Note: Using the -$G- switch may prevent a package from being used in the same application with other packages. Other command-line options may be used, if appropriate, when compiling packages. See "The Command-line compiler" in the online help for information on command-line options not discussed here.

Package files created by a successful compilation

To create a package, you compile a source file that has a .DPK extension. The base name of the .DPK file becomes the base name of the files generated by the compiler. For example, if you compile a package source file called TRAYPAK.DPK, the compiler creates a package called TRAYPAK.BPL.

The following table lists the files produced by the successful compilation of a package.

Table 9.6   Compiled package files

File extension

Contents

DCP

A binary image containing a package header and the concatenation of all DCU files in the package. A single DCP file is created for each package. The base name for the DCP is the base name of the DPK source file.

DCU

A binary image for a unit file contained in a package. One DCU is created, when necessary, for each unit file.

BPL

The runtime package. This file is a Windows DLL with special Delphi-specific features. The base name for the BPL is the base name of the DPK source file.

Deploying packages

Deploying applications that use packages

When distributing an application that uses runtime packages, make sure that your users have the application's .EXE file as well as all the library (.BPL or .DLL) files that the application calls. If the library files are in a different directory from the .EXE file, they must be accessible through the user's Path. You may want to follow the convention of putting library files in the Windows\System directory. If you use InstallShield Express, your installation script can check the user's system for any packages it requires before blindly reinstalling them.

Distributing packages to other developers

If you distribute runtime or design-time packages to other Delphi developers, be sure to supply both .DCP and .BPL files. You will probably want to include .DCU files as well.

Package collection files

Package collections (.DPC files) offer a convenient way to distribute packages to other developers. Each package collection contains one or more packages, including BPLs and any additional files you want to distribute with them. When a package collection is selected for IDE installation, its constituent files are automatically extracted from their .PCE container; the Installation dialog box offers a choice of installing all packages in the collection or installing packages selectively.

To create a package collection,

  1. Choose Tools|Package Collection Editor to open the Package Collection editor.
  2. Click the Add Package speed button, then select a BPL in the Select Package dialog and click Open. To add more BPLs to the collection, click the Add Package speed button again. A tree diagram on the left side of the Package editor displays the BPLs as you add them. To remove a package, select it and click the Remove Package speed button.
  3. Select the Collection node at the top of the tree diagram. On the right side of the Package Collection editor, two fields will appear:

  4. In addition to BPLs, your package collection can contain .DCP, .DCU, and .PAS (unit) files, documentation, and any other files you want to include with the distribution. Ancillary files are placed in file groups associated with specific packages (BPLs); the files in a group are installed only when their associated BPL is installed. To place ancillary files in your package collection, select a BPL in the tree diagram and click the Add File Group speed button; type a name for the file group. Add more file groups, if desired, in the same way. When you select a file group, new fields will appear on the right in the Package Collection editor,

  5. You can select installation directories for the packages listed in the requires clause of any package in your collection. When you select a BPL in the tree diagram, four new fields appear on the right side of the Package Collection editor:

  6. To save your package collection source file, choose File|Save. Package collection source files should be saved with the .PCE extension.
  7. To build your package collection, click the Compile speed button. The Package Collection editor generates a .DPC file with the same name as your source (.PCE) file. If you have not yet saved the source file, the editor queries you for a file name before compiling.

To edit or recompile an existing .PCE file, select File|Open in the Package Collection editor.