Home » Tutorials » Object Pascal/RTL » Includedateien und Units

Includedateien und Units

Wesen und Wirklichkeit der Unit-Dateien

1. Units, die Delphi selbst erzeugt

Zur Erinnerung noch einmal die Struktur einer Unit:

unit <Bezeichner>
interface
uses <Liste von Units>        { optional }
{ public-Deklarationen }
implementation
uses <Liste von Units>        { optional }
{ private-Deklarationen }
{ Implementierung von Prozeduren und Funktionen }
initialization
{ optionaler Initialisierungs-Quelltext }
finitialization
{ optionaler Quelltext für "Aufräumungsarbeiten" }
end;

Die Kopfzeile einer Unit beginnt mit dem reservierten Wort UNIT, dem der Name der Unit als Bezeichner folgt.
Das nächste Element in einer Unit ist das reservierte Wort INTERFACE, das den Anfang des Interface-Teils einer Unit anzeigt – das ist der Teil einer Unit, der für andere Units oder für Anwendungen, die die Unit verwenden, sichtbar ist.
Eine Unit kann die Deklarationen in anderen Units verwenden, indem sie diese Units in einer USES-Anweisung angibt. Die uses-Anweisung kann an zwei Stellen verwendet werden:

a. Uses unmittelbar hinter dem reservierten Wort INTERFACE

Aller Quelltext in der aktuellen Unit kann die in den Interface-Teilen deklarierten Deklarationen der in der Uses-Anweisung angegebenen Units verwenden.
Nehmen wir beispielsweise an, du schreibst Quelltext in der Unit A, und du möchtest eine Prozedur aufrufen, die im Interface-Teil von Unit B deklariert ist. Sobald du den Namen von Unit B in die Uses-Anweisung im Interface-Teil von Unit A aufnimmst, kann jeglicher Quelltext in Unit A die in Unit B deklarierte Prozedur aufrufen.
Ist Unit B in der Uses-Anweisung im Interface-Teil von Unit A enthalten, dann kann Unit A nicht in der Uses-Anweisung im Interface-Teil von Unit B auftauchen. Andernfalls würde eine zirkuläre Unit-Referenz vorliegen, und Delphi würde beim Versuch, eine Compilierung durchzuführen, eine Fehlermeldung ausgeben.

b. Uses unmittelbar hinter dem reservierten Wort IMPLEMENTATION

Nur Deklarationen in der aktuellen Unit können die Deklarationen in den Interface-Teilen der hier angegebenen Units verwenden. Alle anderen Units, die die aktuelle Unit verwenden, haben keinen Zugriff auf die in dieser Uses-Anweisung verzeichneten Units.
Ist zum Beispiel Unit C in einer Uses-Anweisung im Implementation-Teil von Unit B aufgeführt und Unit B in einer Uses-Anweisung von Unit A, dann kann Unit A nur die Deklarationen in den Interface-Teilen von Unit B verwenden. Unit A kann nicht auf die Deklarationen in Unit C zugreifen, es sei denn, Unit C steht in einer Uses-Anweisung von Unit A.
Wenn Unit B in der Uses-Anweisung im Implementation-Teil von Unit A verzeichnet ist, kann Unit A in der Uses-Anweisung im Implementation-Teil von Unit B stehen.
Zirkuläre Unit-Referenzen im Interface-Teil sind unzulässig, aber im Implementation-Teil problemlos möglich.

c. Der Interface-Teil

Der Interface-Teil einer Unit beginnt mit dem reservierten Wort INTERFACE, das hinter der Kopfzeile einer Unit steht, und er endet mit dem reservierten Wort IMPLEMENTATION. Die Schnittstelle (interface) bestimmt, was für eine Anwendung oder eine andere Unit, die diese Unit verwendet, zugänglich ist.
In der Schnittstelle einer Unit kannst du all das deklarieren, was du an Konstanten, Datentypen und Variablen in deinem Programm verwenden möchtest (denke an unsere Sammlung aus Seite 2), dazu noch alle gewünschten Prozeduren und Funktionen.
Diese Prozeduren und Funktionen, die also für eine beliebige Unit oder Anwendung sichtbar sind, sind im Interface-Teil der Unit nur mit ihren Kopfzeilen aufgeführt. Der eigentliche Quelltext, also deren Implementierung, befindet sich dagegen im Implementations-Teil der Unit.
In der Schnittstelle kann eine optionale Uses-Anweisung verwendet werden; sie muss direkt auf das reservierte Wort Interface folgen.

d. Der Implementation-Teil

Der Implementation-Teil einer Unit beginnt mit dem reservierten Wort IMPLEMENTATION. Alles, was im Interface-Teil deklariert ist, ist für den Quelltext im Implementation-Abschnitt zugänglich.
Die Implementation kann über eigene, zusätzliche Deklarationen verfügen, die aber für andere Units oder Anwendungen nicht zur Verfügung stehen.
„Aus dem Implementation-Teil dringt nichts nach außen.“
Die Deklarationen hier im Implementation-Teil werden von den in dieser Unit deklarierten Prozeduren, Funktionen und Ereignisbehandlungsroutinen verwendet.
Optional kann im Implementation-Teil eine Uses-Anweisung eingesetzt werden, die direkt auf das reservierte Wort IMPLEMENTATION folgen muss.
Der Quelltext der in der Schnittstelle Interface deklarierten Routinen muss im Implementation-Teil stehen. In diesem Abschnitt können aber auch Deklarationen von „Sammlungsdaten“ wie in Schritt 2 und von Prozeduren und Funktionen erfolgen, die nur hier (und nicht im Interface-Teil) aufgeführt werden. Der Gültigkeitsbereich bezieht sich dann dementsprechend nur auf diesen Abschnitt. Wir wissen ja: „Aus dem Implementation-Teil dringt nichts nach außen.“

e. Der Initialization-Teil

Wenn Daten, die eine Unit benutzt, auf korrekte Anfangswerte gesetzt werden sollen (initialisieren), dann erreicht man das durch Einfügen eines INITIALIZATION-TEIL in die Unit. Das reservierte Wort INITIALIZATION wird – wie in Schritt 3 aufgezeigt – in die Unit eingesetzt. Zwischen diesen beiden Wörtern INITIALIZATION und END schreibst du deinen Quelltext, der die Daten initialisiert.

initialization
  {Hier steht der Initialisierungs-Quellcode}
end.

Wenn eine Anwendung eine Unit verwendet, wird zuerst der Quelltext im Initialization-Abschnitt der Unit aufgerufen, bevor anderer Quelltext der Anwendung ausgeführt wird. Wenn die Anwendung mehr als eine Unit verwendet, wird der Quelltext aller Initialisierungsteile aufgerufen, ehe der Rest der Anwendung ausgeführt wird.

f. Der Finalization-Teil

Wenn die Unit einige Aufräumarbeiten ausführen muss, wenn einige Ressourcen freigegeben werden müssen, die im Initialization-Teil initialisiert wurden, kann das in einem anschließenden FINALIZATION-Teil ausgeführt werden.
Dieser Abschnitt wird – wie unter Schritt 3 zu ersehen – ans Ende angefügt. Wo ein finalization-Abschnitt verwendet werden soll, muss vorher ein initialization-Teil vorhanden sein.

initialization
  { Hier steht der Quelltext zur Initialisierung }
finalization
  { Hier kommt der Quelltext fü2r die "Aufräumarbeiten" hin }
end.

Wenn eine Anwendung unterbrochen wird, führt sie die finalization-Abschnitte der Unit in umgekehrter Reihenfolge aus.

2. Units, die der Nutzer erzeugt

Du kannst grundsätzlich jeder Unit, die Delphi erzeugt, Quelltext hinzufügen. Das ist kein Problem. Aber du solltest den von Delphi generierten Quelltext auf keinen Fall verändern. Nur wenn Delphi diesen Text verändert, wird das System diese Änderung registrieren und entsprechend darauf reagieren. Im anderen Fall weiß Delphi nichts von deiner Änderung: Die Folge kann ein Absturz des Systems sein.
Um eine eigene Unit zu erzeugen, kannst Du folgendermaßen vorgehen:
Wähle Datei|Neu|Unit aus und klicke auf .
Delphi stellt Dir daraufhin ein Gerüst für Deine Unit zur Verfügung.
So sieht der Quelltext „deiner“ Unit jetzt aus:

unit Unit2;
interface
implementation
initialization
end.

Der Name, den Delphi der neuen Unit gibt, richtet sich nach der Anzahl der in deinem Projekt bereits vorhandenen Units. Du kannst dieses Grundgerüst anschließend mit dem von dir gewünschten Quelltext auffüllen.
Bitte sieh dir dazu auch noch einmal das an, was zu Anfang dieses Abschnittes über Units gesagt wurde, die Delphi selbst erzeugt. So wirst du manches besser verstehen.
Um dieses Dateiengerüst erst einmal zu sichern, speicherst du es unter uDefin.pas in Deinem Projekt-verzeichnis ab.
Wenn du dein Projekt jetzt compilierst, wirst du in dem entsprechenden Verzeichnis eine neue Datei uDefin.dcu vorfinden. Diese neu erzeugte Datei mit der Endung *.dcu (Delphi-compilierte-Unit) enthält Maschinencode, der zur Programmdatei deines Projekts hinzugelinkt wird.
Wenn du diese Unit mit Uses in ein Programm einfügen würdest, gäbe es keinerlei Reaktion; denn die *.pas-Datei uDefin.pas ist ja noch leer.
Rufen wir sie also wieder auf und fügen wir die „Sammlung“ aus Abschnitt 2 – mit nur einer (!) Includedatei – in den Abschnitt nach interface ein. Wenn wir das Programm jetzt nochmals compilieren, kann diese Definitionsdatei in ein Programm mit Uses eingebunden werden, um dort von verschiedenen Stellen des Projekts aus angesprochen zu werden.
Wir wollen die Gelegenheit nutzen, um an dieser Stelle einen ganz praktischen Hinweis zu geben: Wenn ich des öfteren Programme mit Delphi schreibe, werde ich ja oftmals auch die gleichen Prozeduren und Funktionen verwenden. Um diese nicht jedes Mal neu schreiben zu müssen, kann ich diese – wie zuvor beschrieben – in eine eigene Unit einfügen. Damit schaffe ich mir mein eigenes „OwnDelphi 1.0“.
Die Datei OwnDelph.pas könnte dann so aussehen:

unit OwnDelph;

interface

uses
  Windows, Forms, SysUtils, Controls, ...;

procedure Pause (Zeit: LongInt);
// Realisiert eine Pause von <Zeit> Millisekunden

implementation

procedure Pause (Zeit: LongInt);
// Pause in Millisekunden
var
  ZeitVar: LongInt;
begin
  ZeitVar := GetTickCount;   // eine API-Funktion ohne Parameter
  repeat
    Application.ProcessMessages
  until (GetTickCount - ZeitVar > Zeit)
end {Pause};

end.

Das als Beispiel für eine eigene Unit, die nach und nach entsprechend erweitert werden kann.
In einem Programm eingebunden sieht das dann so aus:
unit Formular;
interface

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

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

var
Form1: TForm1;

implementation

{$R *.DFM}

end.