Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Frage zu try..except


AScomp - Di 05.01.10 07:51
Titel: Frage zu try..except
Guten Morgen,

mit try..except kann ich im Normalfall ja das Auslösen bzw. die Anzeige von Exceptions verhindern. Bisher ging ich davon aus, dass


Delphi-Quelltext
1:
2:
3:
4:
try
   bla;
except
end;


wirklich ALLE möglicherweise beim Aufruf von "bla" auftretenden Exceptions abgefangen werden und der Programmablauf nicht abgebrochen wird.

In einem anderen Forenbereich hatte ich jetzt von einem Problem mit folg. Sourcecode berichtet:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
     try
        if CreateLogfile then begin
           AssignFile(LogDatei, AppDataDir + 'mk.log');
           if FileExists(AppDataDir + 'mk.log'then
              Append(LogDatei)
           else
              ReWrite(LogDatei);
           WriteLn(LogDatei, Info);
        end;
     finally
        try
           CloseFile(LogDatei);
        except
        end;
        Application.ProcessMessages;
     end;


Trotz try..finally wurde nämlich beim Aufruf von "Append(LogDatei)" eine Exception ausgelöst, die von madExcept behandelt wurde.

Inzwischen hab ich die entsprechenden, möglicherweise auftretenden Exceptions in obigem Sourcecode durch den Schalter {$I-} erstmal deaktiviert.

Dann noch folgender Fall:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
        try
           FormProgress.VCLZipVerify.ReadZip;
           FormProgress.VCLZipVerify.CheckArchive;
        except
           On EUserCanceled do
              ExecutionCancelled := true;
        end;


Ich ging bisher anscheinend fälschlicherweise davon aus, dass mit diesem Sourcecode alle Exceptions abgefangen werden und nur im Falle der Exception EUserCancelled die Variable noch auf true gesetzt wird.

Doch anscheinend ist das falsch. Der obige Sourcecode fängt nur eine einzige Exception ab, eben EUserCancelled. Alle weiteren Exceptions bleiben unbehandelt und werden normal ausgegeben. Um das zu verhindern, müsste ich wohl den Sourcecode wie folgt abändern:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
        try
           FormProgress.VCLZipVerify.ReadZip;
           FormProgress.VCLZipVerify.CheckArchive;
           except
              On EUserCanceled do
                 ExecutionCancelled := true;
           else
              // alle anderen Exceptions hier abfangen
        end;


Doch was heißt das jetzt genau für:


Delphi-Quelltext
1:
2:
3:
4:
try
   bla;
except
end;


Heißt das, dass in diesem Beispiel ALLE Exceptions abgefangen werden (da im except-Abschnitt keine Exception explizit behandelt wird)? Und würde sich das Verhalten ändern, sobald ich im except-Abschnitt ein on EIrgendwas do einfüge (nämlich derart, dass nur noch diese eine Exception abgefangen wird, während alle anderen unbehandelt bleiben und entsprechend auch vom Programm ausgegeben werden?)?

Danke schonmal, falls jemand meinen Ausführungen überhaupt folgen konnte - wurde ja ein richtiger Roman. ;)


Delete - Di 05.01.10 10:46

user profile iconAScomp hat folgendes geschrieben Zum zitierten Posting springen:

Doch was heißt das jetzt genau für:


Delphi-Quelltext
1:
2:
3:
4:
try
   bla;
except
end;


Heißt das, dass in diesem Beispiel ALLE Exceptions abgefangen werden (da im except-Abschnitt keine Exception explizit behandelt wird)? Und würde sich das Verhalten ändern, sobald ich im except-Abschnitt ein on EIrgendwas do einfüge (nämlich derart, dass nur noch diese eine Exception abgefangen wird, während alle anderen unbehandelt bleiben und entsprechend auch vom Programm ausgegeben werden?)?

Ja. Allerdings ist es keine gute Idee Exceptions nicht zu behandeln. Ist ungefähr so, als wenn du im Auto die Tankanzeige ausbaust.


AScomp - Di 05.01.10 12:32

Das ist soweit klar - danke!


jakobwenzel - Di 05.01.10 15:19

user profile iconAScomp hat folgendes geschrieben Zum zitierten Posting springen:
Trotz try..finally wurde nämlich beim Aufruf von "Append(LogDatei)" eine Exception ausgelöst, die von madExcept behandelt wurde

Das soll auch so, try...finally ist dazu da um etwas (beispielsweise das Freigeben von Objekten) auch dann durchzuführen wenn eine Exception auftrat. Gefangen wird die Exception nur im try...except.


AScomp - Di 05.01.10 15:34

user profile iconjakobwenzel hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconAScomp hat folgendes geschrieben Zum zitierten Posting springen:
Trotz try..finally wurde nämlich beim Aufruf von "Append(LogDatei)" eine Exception ausgelöst, die von madExcept behandelt wurde

Das soll auch so, try...finally ist dazu da um etwas (beispielsweise das Freigeben von Objekten) auch dann durchzuführen wenn eine Exception auftrat. Gefangen wird die Exception nur im try...except.


Sicher? Ich bezweifle, dass es so ist. Im Beitrag http://www.delphi-forum.de/topic_AssignFile+und+tryfinally_96919.html kam das auch so rüber, als ob finally eher die Exception "schlucken" würde.

Demzufolge müsste man dann except und finally auch gemeinsam nutzen können.

Also beispielsweise sowas:



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
     try
        if CreateLogfile then begin
           AssignFile(LogDatei, AppDataDir + 'mk.log');
           if FileExists(AppDataDir + 'mk.log'then
              Append(LogDatei)
           else
              ReWrite(LogDatei);
           WriteLn(LogDatei, Info);
        end;
     except
     finally
        try
           CloseFile(LogDatei);
        except
        end;
        Application.ProcessMessages;
     end;


Sprich: Wenn im try-Abschnitt eine Exception auftritt, soll diese "geschluckt" werden und der finally-Abschnitt abgearbeitet werden.


JDF - Di 05.01.10 15:54

Hi,

Zitat:
Demzufolge müsste man dann except und finally auch gemeinsam nutzen können.


ist eine schöne Idee, andere Programmiersprachen können das.

Im Delphi kenne ich nur die Schachtelung von try..except und try..finally.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
procedure Test;
begin
  TRY
    Objekt.Erstellung;
    TRY
      TRY
        Objekt.Nutzung;
      EXCEPT
        Objekt-Anhängige Fehlerbearbeitung;
      END;
    FINALLY
      Objekt.Beendigung;
    END;
  EXCEPT
    Objekt-Unabhängige Fehlerbearbeitung;
  END;
end;


Ohje, der Konstrukt sieht schlimm aus.

Gruß
Jürgen


AScomp - Di 05.01.10 16:04


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
    TRY
      TRY
        Objekt.Nutzung;
      EXCEPT
        Objekt-Anhängige Fehlerbearbeitung;
      END;
    FINALLY
      Objekt.Beendigung;
    END;


Ich meine, dass das try..finally hier überhaupt keine Wirkung hat. Genauso gut hätte man doch folg. machen können:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
      TRY
        Objekt.Nutzung;
      EXCEPT
        Objekt-Anhängige Fehlerbearbeitung;
      END;
      Objekt.Beendigung;


Bzw. falls im except nicht alle möglichen Exceptions abgefangen werden:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
      TRY
        Objekt.Nutzung;
      EXCEPT
        Objekt-Anhängige Fehlerbearbeitung;
      ELSE
      END;
      Objekt.Beendigung;


jakobwenzel - Di 05.01.10 16:10

Wenn hier aber in der Objekt-Anhängigen Fehlerbearbeitung nochmal ne Exception auftritt hast du ein nicht freigegebenes Objekt.


AScomp - Do 11.03.10 20:42

Hallo,

hätte nochmal eine Frage hierzu:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
function _GetFileSize(const FileName: string): Int64;
var
  FileStream: TFileStream;
begin
  Result := -1;
  try
    try
       FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
       Result := FileStream.Size;
    except
    end;
  finally
    try
      FileStream.Free;
    except
    end;
  end;
end;


Geht das nicht einfacher?

Ein


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
try
  ..
except
  ..
finally
  ..
end;


gibt's ja anscheinend nicht.

Wie löse ich also obige Mini-Funktion sauber?

Das try..finally bräuchte ich ja eigentlich nicht, da ich die Exceptions ohnehin fange und der zweite try..except-Block somit ja in jedem Fall aufgerufen wird, richtig?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
function _GetFileSize(const FileName: string): Int64;
var
  FileStream: TFileStream;
begin
  Result := -1;
  try
     FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
     Result := FileStream.Size;
  except
  end;
  try
    FileStream.Free;
  except
  end;
end;


Sieht auch nicht viel besser aus.

Vielen Dank!


BenBE - Do 11.03.10 20:58


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
function _GetFileSize(const FileName: string): Int64;
var
  FileStream: TFileStream;
begin
  Result := -1;
  try
    FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
    try
       Result := FileStream.Size;
    finally
      FileStream.Free;
    end;
  except
    //Catch the exception
  end;
end;


AScomp - Do 11.03.10 21:13

Danke dir, das hatte ich auch schon - was mich stört ist, dass es zwei try-Blöcke sind.

Aber da es keine try..except..finally-Blöcke zu geben scheint, wird das wohl die einzige Möglichkeit sein.


Delete - Do 11.03.10 21:24

user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
function _GetFileSize(const FileName: string): Int64;
var
  FileStream: TFileStream;
begin
  Result := -1;
  try
    FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
    try
       Result := FileStream.Size;
    finally
      FileStream.Free;
    end;
  except
    //Catch the exception
  end;
end;

Was soll der äußere try-except-Block? Und was sagt der Delphi Compiler dazu?

Richtig wäre:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
Objekt erstellen
try
  try
    mit Objekt arbeiten
  except
    Excpetions behandeln
  end
finally
  Objekt freigeben
end


AScomp - Do 11.03.10 21:29

Warum? Ich möchte ja, dass keine Exception angezeigt wird.

Wenn in deinem Beispiel der FileStream nicht erstellt werden kann, wird trotzdem der finally-Abschnitt aufgerufen und versucht, den FileStream wieder freizugeben.


Delete - Do 11.03.10 21:46

dann so rum:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
try
  <Resource belegen>
  try
    <Mit der Resource arbeiten>
  finally
    <Resource freigeben>
  end;
except
  <Ausnahme behandeln>
end;


Aber was für ein Grund gibt es einen aufgetretenen Fehler unterdrücken zu wollen? Der Benutzer wäre über eine aussagekräftige Fehlermeldung bestimmt sehr erfreut, als einfach so im dunklen stehen gelassen zu werden und eventuell einen Datenverlust zu erleiden.


AScomp - Do 11.03.10 21:51

Dass ich die Exception DORT nicht abfange heißt ja nicht, dass sie überhaupt nicht abgefangen wird. Nur eben nicht an dieser Stelle. ;)

Zitat:

dann so rum:


So hat es BenBe doch vorgeschlagen, oder entgeht mir schon wieder was? :D


Delete - Do 11.03.10 22:10

Mist, stimmt. Ich schiebe das jetzt mal auf meine Kopfschmerzen.


AScomp - Do 11.03.10 22:11

Gute Besserung!


BenBE - Fr 12.03.10 01:23

@Luckie: Die Fehlerbehandlung wird in dieser Routine basierend auf dem Result erledigt. Sauberer wäre es aber mit CreateFile die Datei zu öffnen und über das Handle wenn möglich einen THandleStream aufzubauen. Wobei man für diesen Fall auch gleich komplett über die WinAPI gehen könnte, was sogar die Nutzung einer Exception hier ganz vermeidet ...

P.S.: Sollte man schlafen gehen, wenn man im vorigen Post "Gute Bescherung" liest?


Martok - Fr 12.03.10 03:34

Die Kombination CreateFile/GetFileSizeEx/CloseHandle ist an der Stelle tatsächlich vorzuziehen.
Allein schon, weil man dann volle Kontrolle über Fehlercodes hat, und außerdem um den Aufwand der Klasseninstantiierung herum kommt.

P.S.: Ja, direkt bis zum Dezember durchschlafen...