Beiträge durchsuchen

Programmaufbau

Projektdatei: Anwendung

Eine in Delphi erstellte Anwendung (auch Projekt genannt) besteht aus einer Projektdatei mit der Endung .dpr, die das Hauptprogramm enthält, und evtl. einer oder mehreren Units.
Der Aufbau der Projektdatei sieht so aus, wenn es sich um eine Anwendung mit grafischer Benutzeroberfläche (GUI) handelt:

program Project1;

uses
 Forms,
 Unit1 in 'Unit1.pas' {Form1};

{$R *.RES}

begin
 Application.Initialize;
 Application.CreateForm(TForm1, Form1);
 Application.Run;
end.

Das Schlüsselwort program legt fest, dass aus dem Projekt nach dem Compilieren eine ausführbare Anwendung wird. Über Datei/Neu/Konsolenanwendung ist es jedoch auch möglich ein Programm mit einer DOS-ähnlichen Oberfläche zu erstellen. Dabei hat die dpr-Datei folgenden Aufbau:

program Project2;
{$APPTYPE CONSOLE}
uses sysutils;

begin
 // Hier Anwender-Code
end.

Jedes Delphi-Projekt besteht also aus einem Hauptprogramm, das sich in der Projektdatei befindet. Erstellt man Programme mit einer grafischen Oberfläche, muss diese Projektdatei normalerweise nicht bearbeitet werden. Ansehen kann man sie sich über das Menü „Projekt“ / „Quelltext anzeigen“.

Projektdatei: Library

Beginnt eine Projektdatei mit dem Schlüsselwort library, so wird nach dem Compilieren eine DLL (Dynamic Link Library unter Windows) bzw. .so (Shared Object unter Linux) daraus. Eine solche Bibliothek enthält Programmcode, der von anderen Anwendungen verwendet werden kann. Mehr darüber erfahren Sie in unserem Tutorial zu dem Thema.

Projektdatei: Package

Packages stellen eine Sammlung von Units dar und unterscheiden sich im Prinzip nicht von DLLs. Als Quelldatei hat die Projektdatei nicht die Endung .dpr, sondern .dpk für Delphi Package. Sie beginnt mit dem Schlüsselwort package. Der Aufbau sieht wie folgt aus:

package ;

requires
 

contains


end.

Packages haben unter Win32 die Endung .bpl (Borland Package Library). Unter .NET sind es Assemblies mit der Endung .dll.

Units

Um nicht allen Code in eine einzige Datei schreiben zu müssen, gibt es das Konzept der Units. Der Entwickler kann seinen Code, z.B. nach Aufgaben geordnet, in verschiedene Programmmodule aufteilen. Diese Programmmodule (Units, auch Bibliotheken genannt) haben die Dateiendung .pas und folgenden Aufbau:

unit ;

interface

uses ;



implementation

uses 

   

initialization


finalization


end.

Solch ein Grundgerüst erhält man, wenn man in Delphi über das Menü Datei Neu/Unit auswählt. Die Abschnitte initialization und finalization sind optional.
Jede Unit beginnt in der ersten Zeile mit dem Schlüsselwort unit. Dahinter folgt der Name der Unit, der nicht von Hand bearbeitet werden darf. Er entspricht dem Dateinamen (ohne die Endung pas) und wird von Delphi beim Speichern der Unit automatisch angepasst.

Nun folgen zwei Schlüsselwörter, die jeweils einen neuen Abschnitt einleiten: der interface- und der implementation-Teil. Eine Unit endet mit dem Schlüsselwort end gefolgt von einem Punkt.
Wie im Kapitel über Prozeduren und Funktionen beschrieben, wird im interface-Teil lediglich der Kopf der im implementation-Teil befindlichen Prozeduren und Funktionen aufgeführt. So sieht man auf einen Blick, was sich in einer Unit alles befindet.

Als Beispiel schreiben wir nun eine Unit, die lediglich eine Funktion zur Mehrwertsteuerberechnung enthält:

unit Unit1;

interface
 function Brutto(netto: real): real;

implementation

function Brutto(netto: real): real;
begin
 result := netto * 1.16;
end;

end.

Wird einer Anwendung ein neues Fenster hinzugefügt, so gehört dazu immer auch eine Unit. Dagegen kann man zur besseren Strukturierung seines Codes beliebig viele Units einsetzen, die nicht mit einem Fenster in Verbindung stehen.
Beim Compilieren wird aus jeder .pas-Datei eine .dcu-Datei (Delphi Compiled Unit) bzw. eine .dcuil (Delphi Compiled Unit Intermediate Language) in .NET erzeugt.
Die Unit System wird automatisch in jede Unit und jedes Hauptprogramm eingebunden, ohne dass sie extra erwähnt wird. Auf alle dort definierten Routinen kann also jederzeit zugegriffen werden.

Units verwenden

Mit solch einer Unit alleine kann man nicht viel anfangen. Wir müssen sie in eine Anwendung einbinden. Um die Funktion Brutto jedoch aus einer anderen Unit oder dem Hauptprogramm aufrufen zu können, müssen wir sie dort bekannt machen. Das geschieht über das Schlüsselwort uses, das bereits im ersten Beispiel zur Projektdatei oben zu sehen ist. Wichtig ist hierbei, dass jede Unit innerhalb eines Projekts einen eindeutigen Namen haben muss. Man kann nicht in Unit1 eine weitere Unit1 einbinden. Deshalb speichern wir unsere Beispiel-Unit unter dem Namen „beispiel.pas“. Die erste Zeile ändert sich nun automatisch in unit beispiel;.
Nun legen wir über das Datei-Menü eine neue Anwendung an. In die Unit1 binden wir unsere Unit ein:

unit Unit1;

interface

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

type
 TForm1 = class(TForm)
 private
 { Private-Deklarationen }
 public
 { Public-Deklarationen }
 end;

var
 Form1: TForm1;

implementation

{$R *.DFM}

end.

Nun können wir in der gesamten Unit unsere Funktion Brutto verwenden, als wäre die Funktion in der gleichen Unit implementiert. Gibt es in einer anderen eingebunden Unit eine Funktion/Prozedur gleichen Namens, muss zuerst der Unit-Namen genannt werden, um klarzustellen, welche Funktion gemeint ist, z.B. ergebnis := Beispiel.Brutto(1500);

Positionen der uses-Klausel

Werden Units eingebunden, die bereits im Interface benötigt werden (z.B. weil in ihnen Typen definiert sind), so werden sie der uses-Klausel im interface-Teil eingefügt. In allen anderen Fällen sollte eine Unit der uses-Klausel des implementation-Abschnitts hinzugefügt werden.
Haben wir z. B. ein Projekt mit zwei Formularen und den dazugehörigen Units unit1 und unit2, und soll Form2 (in unit2) aus Form1 aufgerufen werden, so braucht die Unit1 Zugriff auf die Unit2. Dazu wechseln wir in die Anzeige von Unit1 und klicken im Datei-Menü von Delphi auf „Unit verwenden“. In dem erscheinenden Fenster sind alle Units des Projekts aufgelistet, die von der aktuellen Unit noch nicht verwendet werden. Hier wählen wir „Unit2“ aus und schließen das Fenster. Delphi hat nun automatisch eine Zeile direkt am Anfang des implementation-Abschnitts eingefügt. Folgendes Beispiel zeigt diesen Fall, wobei Form1 einen Button (Button1) enthält, auf dessen Klick Form2 geöffnet wird:

unit Unit1;

interface

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

type
 TForm1 = class(TForm)
 Button1: TButton;
 procedure Button1Click(Sender: TObject);
 private
 { Private-Deklarationen }
 public
 { Public-Deklarationen }
 end;

var
 Form1: TForm1;

implementation

uses Unit2;

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
 Form2.ShowModal;
end;

end.

Die Verwendung der zweiten uses-Klausel hat ihren Grund darin, dass zwei Units sich nicht gegenseitig im uses-Abschnitt des Interface einbinden können (zirkuläre Unit-Referenz). Im Implementation-Teil ist dies jedoch möglich.
Allgemein gilt, dass Units, deren Routinen nur für den implementation-Abschnitt benötigt werden, auch im implementation-Abschnitt eingebunden werden. Wird dagegen ein Teil einer anderen Unit (z.B. ein selbstdefinierter Datentyp) bereits im interface-Abschnitt benötigt, muss die Unit natürlich schon dort eingebunden werden.

interface und implementation

Der interface-Abschnitt (zu deutsch „Schnittstelle“) dient dazu, Funktionen, Prozeduren, Typen usw. dieser Unit anderen Units zur Verfügung zu stellen. Alles, was hier steht, kann von außen verwendet werden. Bei Prozeduren und Funktionen steht hier nur der Kopf der Routine (Name, Parameter und evtl. Rückgabewert). Die Definition folgt dann im implementation-Abschnitt.
Der interface-Abschnitt endet mit Beginn des implementation-Abschnitts.

initialization und finalization

Bei Bedarf können am Ende einer Unit noch zwei Abschnitte stehen: initialization und finalization.
Initialization muss dabei als erstes aufgeführt werden. Hier werden alle Befehle aufgeführt, die bei Programmstart der Reihe nach ausgeführt werden sollen. Danach folgt das Ende der Unit (end.) oder der finalization-Abschnitt. Dieser ist das Gegenstück zu initialization. Hier können z. B. vor Programmende Objekte freigegeben werden.
Der Aufbau einer Units sieht dann so aus:

unit Unit1;

interface

implementation

initialization

finalization

end.

finalization kann nur verwendet werden, wenn es auch einen initialization-Abschnitt gibt; initialization kann jedoch auch ohne finalization vorkommen. Eine Unit funktioniert allerdings auch ohne diese Abschnitte.