Programs and Units (Delphi)
Go Up to Programs and Units Index
This topic covers the overall structure of a Delphi application: the program header, unit declaration syntax, and the uses clause.
- Divide large programs into modules that can be edited separately.
- Create libraries that you can share among programs.
- Distribute libraries to other developers without making the source code available.
Contents
Program Structure and Syntax
A complete, executable Delphi application consists of multiple unit modules, all tied together by a single source code module called a project file. In traditional Pascal programming, all source code, including the main program, is stored in .pas files. Embarcadero tools use the file extension .dpr to designate the main program source module, while most other source code resides in unit files having the traditional .pas extension. To build a project, the compiler needs the project source file, and either a source file or a compiled unit file for each unit.
The source code file for an executable Delphi application contains:
- a program heading,
- a uses clause (optional), and
- a block of declarations and executable statements.
The compiler, and hence the IDE, expect to find these three elements in a single project (.dpr) file.
The Program Heading
The program heading specifies a name for the executable program. It consists of the reserved word program, followed by a valid identifier, followed by a semicolon. For applications developed using Embarcadero tools, the identifier must match the project source file name.
The following example shows the project source file for a program called Editor. Since the program is called Editor, this project file is called Editor.dpr.
program Editor; uses Forms, REAbout, // An "About" box REMain; // Main form {$R *.res} begin Application.Title := 'Text Editor'; Application.CreateForm(TMainForm, MainForm); Application.Run; end.
The first line contains the program heading. The uses clause in this example specifies a dependency on three additional units: Forms, REAbout, and REMain. The $R compiler directive links the project's resource file into the program. Finally, the block of statements between the begin and end keywords are executed when the program runs. The project file, like all Delphi source files, ends with a period (not a semicolon).
Delphi project files are usually short, since most of a program's logic resides in its unit files. A Delphi project file typically contains only enough code to launch the application's main window, and start the event processing loop. Project files are generated and maintained automatically by the IDE, and it is seldom necessary to edit them manually.
In standard Pascal, a program heading can include parameters after the program name:
program Calc(input, output);
Embarcadero's Delphi ignores these parameters.
In RAD Studio, the program heading introduces its own namespace, which is called the project default namespace.
The Program Uses Clause
The uses clause lists those units that are incorporated into the program. These units may in turn have uses clauses of their own. For more information on the uses clause within a unit source file, see Unit References and the Uses Clause, below.
The uses clause consists of the keyword uses, followed by a comma-delimited list of units the project file directly depends on.
The Block
The block contains a simple or structured statement that is executed when the program runs. In most program files, the block consists of a compound statement bracketed between the reserved words begin and end, whose component statements are simply method calls to the project's Application object. Most projects have a global Application variable that holds an instance of Vcl.Forms.TApplication, Web.WebBroker.TWebApplication, or Vcl.SvcMgr.TServiceApplication. The block can also contain declarations of constants, types, variables, procedures, and functions; these declarations must precede the statement part of the block. Note that the end that represents the end of the program source must be followed by a period (.):
begin . . . end.
Unit Structure and Syntax
A unit consists of types (including classes), constants, variables, and routines (functions and procedures). Each unit is defined in its own source (.pas) file.
A unit file begins with a unit heading, which is followed by the interface keyword. Following the interface keyword, the uses clause specifies a list of unit dependencies. Next comes the implementation section, followed by the optional initialization, and finalization sections. A skeleton unit source file looks like this:
unit Unit1; interface uses // List of unit dependencies goes here... // Interface section goes here implementation uses // List of unit dependencies goes here... // Implementation of class methods, procedures, and functions goes here... initialization // Unit initialization code goes here... finalization // Unit finalization code goes here... end.
The unit must conclude with the reserved word end followed by a period.
The Unit Heading
The unit heading specifies the unit's name. It consists of the reserved word unit, followed by a valid identifier, followed by a semicolon. For applications developed using Embarcadero tools, the identifier must match the unit file name. Thus, the unit heading:
unit MainForm;
would occur in a source file called MainForm.pas, and the file containing the compiled unit would be MainForm.dcu. Unit names must be unique within a project. Even if their unit files are in different directories, two units with the same name cannot be used in a single program.
The Interface Section
The interface section of a unit begins with the reserved word interface and continues until the beginning of the implementation section. The interface section declares constants, types, variables, procedures, and functions that are available to clients. That is, to other units or programs that wish to use elements from this unit. These entities are called public because code in other units can access them as if they were declared in the unit itself.
The interface declaration of a procedure or function includes only the routine's signature. That is, the routine's name, parameters, and return type (for functions). The block containing executable code for the procedure or function follows in the implementation section. Thus procedure and function declarations in the interface section work like forward declarations.
The interface declaration for a class must include declarations for all class members: fields, properties, procedures, and functions.
The interface section can include its own uses clause, which must appear immediately after the keyword interface.
The Implementation Section
The implementation section of a unit begins with the reserved word implementation and continues until the beginning of the initialization section or, if there is no initialization section, until the end of the unit. The implementation section defines procedures and functions that are declared in the interface section. Within the implementation section, these procedures and functions may be defined and called in any order. You can omit parameter lists from public procedure and function headings when you define them in the implementation section; but if you include a parameter list, it must match the declaration in the interface section exactly.
In addition to definitions of public procedures and functions, the implementation section can declare constants, types (including classes), variables, procedures, and functions that are private to the unit. That is, unlike the interface section, entities declared in the implementation section are inaccessible to other units.
The implementation section can include its own uses clause, which must appear immediately after the keyword implementation. The identifiers declared within units specified in the implementation section are only available for use within the implementation section itself. You cannot refer to such identifiers in the interface section.
The Initialization Section
The initialization section is optional. It begins with the reserved word initialization and continues until the beginning of the finalization section or, if there is no finalization section, until the end of the unit. The initialization section contains statements that are executed, in the order in which they appear, on program start-up. So, for example, if you have defined data structures that need to be initialized, you can do this in the initialization section.
For units in the interface uses list, the initialization sections of the units used by a client are executed in the order in which the units appear in the client's uses clause.
The older "begin ... end." syntax still functions. Basically, the reserved word "begin" can be used in place of initialization followed by zero or more execution statements. Code using the older "begin ... end." syntax cannot specify a finalization section. In this case, finalization is accomplished by providing a procedure to the ExitProc variable. This method is not recommended for code going forward, but you might see it used in older source code.
The Finalization Section
The finalization section is optional and can appear only in units that have an initialization section. The finalization section begins with the reserved word finalization and continues until the end of the unit. It contains statements that are executed when the main program terminates (unless the Halt procedure is used to terminate the program). Use the finalization section to free resources that are allocated in the initialization section.
Finalization sections are executed in the opposite order from initialization sections. For example, if your application initializes units A, B, and C, in that order, it will finalize them in the order C, B, and A.
Once a unit's initialization code starts to execute, the corresponding finalization section is guaranteed to execute when the application shuts down. The finalization section must therefore be able to handle incompletely initialized data, since, if a runtime error occurs, the initialization code might not execute completely.
Unit References and the Uses Clause
A uses clause lists units used by the program, library, or unit in which the clause appears. A uses clause can occur in
- the project file for a program, or library
- the interface section of a unit
- the implementation section of a unit
Most project files contain a uses clause, as do the interface sections of most units. The implementation section of a unit can contain its own uses clause as well.
The System unit and the SysInit unit are used automatically by every application and cannot be listed explicitly in the uses clause. (System implements routines for file I/O, string handling, floating point operations, dynamic memory allocation, and so forth.) Other standard library units, such as SysUtils, must be explicitly included in the uses clause. In most cases, all necessary units are placed in the uses clause by the IDE, as you add and remove units from your project.
Case Sensitivity: In unit declarations and uses clauses, unit names must match the file names in case. In other contexts (such as qualified identifiers), unit names are case insensitive. To avoid problems with unit references, refer to the unit source file explicitly:
uses MyUnit in "myunit.pas";
If such an explicit reference appears in the project file, other source files can refer to the unit with a simple uses clause that does not need to match case:
uses Myunit;
The Syntax of a Uses Clause
A uses clause consists of the reserved word uses, followed by one or more comma-delimited unit names, followed by a semicolon. Examples:
uses Forms, Main; uses Forms, Main; uses Windows, Messages, SysUtils, Strings, Classes, Unit2, MyUnit;
In the uses clause of a program or library, any unit name may be followed by the reserved word in and the name of a source file, with or without a directory path, in single quotation marks; directory paths can be absolute or relative. Examples:
uses Windows, Messages, SysUtils, Strings in 'C:\Classes\Strings.pas', Classes;
Use the keyword in after a unit name when you need to specify the unit's source file. Since the IDE expects unit names to match the names of the source files in which they reside, there is usually no reason to do this. Using in is necessary only when the location of the source file is unclear, for example when:
- You have used a source file that is in a different directory from the project file, and that directory is not in the compiler's search path.
- Different directories in the compiler's search path have identically named units.
- You are compiling a console application from the command line, and you have named a unit with an identifier that doesn't match the name of its source file.
The compiler also relies on the in ... construction to determine which units are part of a project. Only units that appear in a project (.dpr) file's uses clause followed by in and a file name are considered to be part of the project; other units in the uses clause are used by the project without belonging to it. This distinction has no effect on compilation, but it affects IDE tools like the Project Manager.
In the uses clause of a unit, you cannot use in to tell the compiler where to find a source file. Every unit must be in the compiler's search path. Moreover, unit names must match the names of their source files.
Multiple and Indirect Unit References
The order in which units appear in the uses clause determines the order of their initialization and affects the way identifiers are located by the compiler. If two units declare a variable, constant, type, procedure, or function with the same name, the compiler uses the one from the unit listed last in the uses clause. (To access the identifier from the other unit, you would have to add a qualifier: UnitName.Identifier.)
A uses clause need include only units used directly by the program or unit in which the clause appears. That is, if unit A references constants, types, variables, procedures, or functions that are declared in unit B, then A must use B explicitly. If B, in turn, references identifiers from unit C, then A is indirectly dependent on C; in this case, C needn't be included in a uses clause in A, but the compiler must still be able to find both B and C in order to process A.
The following example illustrates indirect dependency:
program Prog; uses Unit2; const a = b; // ... unit Unit2; interface uses Unit1; const b = c; // ... unit Unit1; interface const c = 1; // ...
In this example, Prog depends directly on Unit2, which depends directly on Unit1. Hence Prog is indirectly dependent on Unit1. Because Unit1 does not appear in Prog's uses clause, identifiers declared in Unit1 are not available to Prog.
To compile a client module, the compiler needs to locate all units that the client depends on, directly or indirectly. Unless the source code for these units has changed, however, the compiler needs only their .dcu files, not their source (.pas) files.
When a change is made in the interface section of a unit, other units that depend on the change must be recompiled. But when changes are made only in the implementation or other sections of a unit, dependent units don't have to be recompiled. The compiler tracks these dependencies automatically and recompiles units only when necessary.
Circular Unit References
When units reference each other directly or indirectly, the units are said to be mutually dependent. Mutual dependencies are allowed as long as there are no circular paths connecting the uses clause of one interface section to the uses clause of another. In other words, starting from the interface section of a unit, it must never be possible to return to that unit by following references through interface sections of other units. For a pattern of mutual dependencies to be valid, each circular reference path must lead through the uses clause of at least one implementation section.
In the simplest case of two mutually dependent units, this means that the units cannot list each other in their interface uses clauses. So the following example leads to a compilation error:
unit Unit1; interface uses Unit2; // ... unit Unit2; interface uses Unit1; // ...
However, the two units can legally reference each other if one of the references is moved to the implementation section:
unit Unit1; interface uses Unit2; // ... unit Unit2; interface //... implementation uses Unit1; // ...
To reduce the chance of circular references, it's a good idea to list units in the implementation uses clause whenever possible. Only when identifiers from another unit are used in the interface section is it necessary to list that unit in the interface uses clause.