Home » Tutorials » Programmierkonzepte » Fehlermeldungen

Fehlermeldungen

Exceptions benutzen

Schauen wir uns nun einmal an, wie Exceptions benutzt werden. Dazu dient uns folgendes Grundgerüst:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if F1 then
    ShowMessage('Hallo.');
end;
      
function TForm1.F1: Boolean;
begin
  Result := F2('test') <> 0;
end;

function TForm1.F2(s: string): Integer;
begin
  if s = 'test' then
    Result := 0
  else if s = 'test2' then
    Result := 1
  else
    Result := -1;
  F3(Result);
end;
       
procedure TForm1.F3(i: Integer);
begin
  // mach was
end;

Die einzelnen Funktionen symbolisieren die einzelnen Schichten des Programmes. Jede Funktion wird von einer anderen aufgerufen und Die Funktion(bzw. Prozedur) Button1Click ist der Eventhandler eines Buttons. Durch Klick auf den Button werden also alle Funktionen ineinander verschachtelt aufgerufen…
Tritt nun in F3 ein Fehler auf, so wollen wir den Parameter in Button1Click auswerten können. Dazu leiten wir erst einmal eine neue Exception-Klasse ab:

EMyException = class(Exception) // alle Exceptions beginnen per Konvention mit einem E; Die Klasse Exception ist Vorfahr aller Exceptions
private
  FParam: Integer;
public
  constructor Create(Msg: string; Param: Integer);
  property Param: Integer read FParam write FParam;
end;
       
[...]

constructor EMyException.Create(Msg: string; Param: Integer);
begin
  inherited Create(Msg);
  FParam := Param;
end;

Wir haben also nun unsere eigene Exception-Klasse, die wir um eine weitere Eigenschaft und einen entsprechenden Konstruktor erweitert haben.
Mit der Zeile

raise EMyException.Create('Fehler', i);

können wir nun die Exception erzeugen(‚werfen‘).
Durch entsprechende try…except-Blöcke kann nun die Exception abgefangen und behandelt werden:

procedure TForm1.Button1Click(Sender: TObject);
begin
try
if F1 then
ShowMessage(‚Hallo.‘);
except
on E: EMyException do
begin
ShowMessage(‚Eine EMyException ist in Button1Click angekommen. ‚
+ ‚Der Parameter war ‚ + IntToStr(E.Param) + sLineBreak
+ ‚Die Meldung lautet:‘ + sLineBreak + E.Message);
end;
on Exception do // egal, welche Art von Exception es ist
begin          // da aber die EMyException oben schon behandelt wurde, passiert hier nichts
ShowMessage(‚Irgendeine Exception ist in Button1Click angekommen; ‚
+ ‚Exception wird nicht weiter gegeben‘);
end;
end;
end;

function TForm1.F1: Boolean;
begin
try
Result := F2(‚test‘) 0;
except
on E: Exception do   // egal, welche Art von Exception es ist
begin
ShowMessage(‚Eine Exception ist in F1 angekommen.‘);
raise;   // weiterreichen
end;
end;
end;
function TForm1.F2(s: string): Integer;
begin
try
if s = ‚test‘ then
Result := 0
else if s = ‚test2‘ then
Result := 1
else
Result := -1;
F3(Result);
except
on E: EMyException do   // nur, wenn es sich um eine EMyException handelt
begin
E.Message := ‚Meldung aus F2‘ + sLineBreak + E.Message;  // Meldung anhängen
raise; // weiterreichen
end;
end;
end;

procedure TForm1.F3(i: Integer);
begin
raise EMyException.Create(‚Fehler‘, i);  // Exception werfen
end;
Wenn wir das Programm starten, erhalten wir folgende Meldungen:

---------------------------
Project1
---------------------------
Eine Exception ist in F1 angekommen.
---------------------------
OK   
---------------------------

und dann:

---------------------------
Project1
---------------------------
Eine EMyException ist in Button1Click angekommen. Der Parameter war 0
Die Meldung lautet:
Meldung aus F2
Fehler
---------------------------
OK   
---------------------------

Was passiert aber nun, wenn es sich um eine andere Exception-Klasse handelt? Probieren wirs einfach aus. Oben im Code sind schon Teile vorhanden, die sich darum kümmern. Zum Testen, ändern wir die Zeile

raise EMyException.Create('Fehler', i);  // Exception werfen

in

raise Exception.Create('Fehler');  // Exception werfen

Dabei erhalten wir folgende Meldungen:

---------------------------
Project1
---------------------------
Eine Exception ist in F1 angekommen.
---------------------------
OK   
---------------------------

und danach:

---------------------------
Project1
---------------------------
Irgendeine Exception ist in Button1Click angekommen; Exception wird nicht weiter gegeben
---------------------------
OK   
---------------------------

Hier wurde die Exception in F3 geworfen, in F2 automatisch durchgereicht, weil sie dort nicht behandelt wurde, in F1 registriert und weitergereicht und in Button1Click abschießend behandelt.
Schauen wir uns noch einmal Folgendes an:

on E: EMyException do
    begin
      ShowMessage('Eine EMyException ist in Button1Click angekommen. '
        + 'Der Parameter war ' + IntToStr(E.Param) + sLineBreak
        + 'Die Meldung lautet:' + sLineBreak + E.Message);
    end;
    on Exception do // egal, welche Art von Exception es ist
    begin          // da aber die EMyException oben schon behandelt wurde, passiert hier nichts
      ShowMessage('Irgendeine Exception ist in Button1Click angekommen; '
        + 'Exception wird nicht weiter gegeben');
    end;

Was pasiert wohl, wenn wir die beiden on-Blöcke vertauschen? Ganz einfach: Der Untere Block wird in keinem Fall ausgeführt. Auch, nicht, wenn es sich um eine EMyException handelt. Die Exception ist ja schon durch den vorhergehenden Block schon behandelt. Wichtig ist also, dass die allgemeinen on-Blöcke unten stehen und die spezifischen oben…
Wir sehen also, dass alle Informationen von ganz unten nach ganz oben wanden können. Dabei kann jede Schicht auf die Infos zugreifen und diese sogar verändern. Trotzdem müssen wir uns weder Gedanken über das Freigeben der Exception-Klasse(das macht Delphi automatisch), noch über das Durchreichen machen, was mit einem einfachen raise; getan war.
Und sollte eine Exception gar nicht behandelt werden(was eigentlich ein Programmierfehler ist), springt Delphi für einen ein und zeigt dem Benutzer die Fehlermeldung, damit der den Support kontaktieren kann… *zwinker*