|
Tutorial geschlossen
|
Registriert seit: 21. Mär 2005
Was sind Exceptions?
Das Wort "Exception" heißt übersetzt "Ausnahme". Man könnte sagen, wenn eine Exception auftritt, hat man einen "Ausnahmezustand". In der Regel deutet eine Exception darauf hin, daß etwas ziemlich schief gegangen ist. Wie können Exceptions entstehen? Es gibt grundsätzlich zwei verschiedene Möglichkeiten, wie Exceptions entstehen können:
Wenn eine Exception ausgelöst wird, sucht das Betriebssystem nach einem Exception-Handler. Das heißt, das Betriebssystem sucht nach jemandem, der sich für die Exception zuständig fühlt und sich um das Problem kümmert. In den Tiefen des Delphi-Quellcodes sind entsprechende Exception-Handler eingebaut, die die meisten anfallenden Exceptions abfangen und dann versuchen, sinnvoll zu reagieren. Im Normalfall zeigen die Delphi-Exception-Handler eine kleine Box an (siehe Screenshots oben) und führen dann den normalen Programmverlauf fort. Es gibt aber einige Situationen, in denen das nicht so gut klappt, wie gewohnt. In diesen Fällen kommen dann weniger aussagekräftige Boxen wie z.B. diese hier: Oder in noch schlimmeren Fällen kann es dazu kommen, daß ein Thread einfach stillschweigend beendet wird. Oder daß Windows selbst (bzw Dr. Watson) sich über das abgestürzte Programm beschwert. In Windows 9x kann es in Ausnahmefällen nach Exceptions sogar mal zu blauen Bildschirmen kommen. Wieso klappt das behandeln von Exceptions manchmal besser und manchmal schlechter? Diese Frage werden wir uns später nochmal stellen. Im Moment wissen wir noch nicht genug, um das zu verstehen. Wir können wir selbst mit aufgetretenen Exceptions umgehen? Manchmal weiß man (oder befürchtet man), daß ein bestimmter Code eine Exception auslösen kann. In solchen Fällen kann es erwünscht sein, die mögliche Exception selbst abzufangen und entsprechend zu reagieren. Hier ein kleines Beispiel dafür:
Delphi-Quellcode:
Wenn innerhalb eines "try..except"-Blocks eine Exception auftritt, wird diese abgefangen und der Code im "except..end"-Block wird ausgeführt. Das Programm wird direkt nach dem "except..end"-Block fortgesetzt. Die Exception selbst wird in dem Moment aufgelöst. Falls keine Exception auftritt, wird der Code im "except..end"-Block *nicht* ausgeführt.
function CreateEmptyFile(fileName: string) : boolean;
begin try TFileStream.Create(fileName, fmCreate).Free; result := true; except result := false; end; end; Wichtig zu beachten ist, daß beim Auftreten einer Exception das Betriebssystem nach so einem "try..except"-Block sucht. Die aufgerufenen Funktionen werden dabei rückwärts durchgegangen, solange bis endlich ein "try..except"-Block gefunden wurde. Zur Verdeutlichung ein weiteres Beispiel:
Delphi-Quellcode:
Was passiert in diesem Beispiel, wenn *keine* Exception ausgelöst wird? Die "except..end"-Blöcke werden in dem Fall nicht durchlaufen. Also würden wir die Meldungen in folgender Reihenfolge sehen: 1.1, 1.2, 2.1, 3.1, 3.2, 2.2, 1.3 und 1.5.
procedure Function3;
begin ShowMessage('3.1'); if Random(10) = 5 then raise Exception.Create('Zufall!'); ShowMessage('3.2'); end; procedure Function2; begin ShowMessage('2.1'); Function3; ShowMessage('2.2'); end; procedure Function1; begin ShowMessage('1.1'); try ShowMessage('1.2'); Function2; ShowMessage('1.3'); except ShowMessage('1.4'); end; ShowMessage('1.5'); end; Was passiert, wenn eine Exception ausgelöst wird? In dem Fall bekommen wir 1.1, 1.2, 2.1, 3.1, 1.4 und 1.5. Im dem Moment wo die Exception ausgelöst wird, sucht das Betriebssystem nach dem nächst gelegenen "try..except" Block. Solange keiner gefunden wird, werden alle aufgerufenen Funktionen (also hier Function3 und Function2) sofort verlassen. Sobald ein "try..except"-Block gefunden wird, wird der entsprechende "except..end"-Block ausgeführt. Das Programm wird dann direkt nach diesem Block fortgeführt. In bestimmten Ausnahmefällen kann es für ein Programm Sinn machen, die Ausführung einer bestimmten Kette von Funktionen sofort zu unterbrechen und direkt an eine gewünschte Code-Stelle in einer tiefer liegenden Funktion zurückzuspringen. Solche Fälle kann man lösen, indem man manuell eine Exception auslöst und dann an der gewünschten Stelle abfängt. Was passiert mit Exceptions, die ich selbst nicht behandle? Die meisten Exceptions passieren während der Phase eines Programmes, wo das Hauptformular aktiv ist. Diese Phase wird durch "Application.Run" in der Projekt-Datei (*.dpr) gestartet. Delphi hat in neueren Versionen die Haupt-Message-Behandlungs-Schleife in "Application.Run" durch einen "try..except"-Block geschützt. Das heißt, die meisten unbehandelten Exceptions enden in diesem "try..except"-Block. Delphi zeigt dann eine einfache Box an mit einer kleinen Beschreibung der Exception. Nachher läuft die Message-Schleife in "Application.Run" einfach weiter. Wieso kommt Delphi mit manchen Exceptions nicht klar? Es gibt Fälle, in denen Delphi nur einen "Runtime error xxx" anzeigt, statt eine vernünftige Meldung zu bringen. Manchmal fängt Delphi eine Exception auch gar nicht ab, so daß Windows selbst die Exception-Behandlung übernimmt. In beiden Fällen ist eine vernünftige Fortführung des Programmes nicht mehr möglich. Wie kommt es zu diesen Situationen? Die Antwort ist relativ einfach: Exceptions, die außerhalb von "Application.Run" auftreten, sind nicht mehr durch den "try..except"-Block in "Application.Run" geschützt. Delphi fängt manche dieser Exceptions dann in einer noch tieferen Ebene ab. Das funktioniert in etwa so:
Delphi-Quellcode:
Wie man deutlich erkennen kann, gibt es nach dem Anzeigen einer "Runtime error xxx"-Meldung gar keine andere Möglichkeit mehr, als das Programm sofort zu beenden.
program blabla;
begin try Application.Initialize; Application.CreateForm(...); Application.Run; except ShowMessage('Runtime error xxx'); end; ExitProcess(0); end. Eine weitere Art von Exception, die Delphi oft nicht abfängt, sind Exceptions in Threads. In neueren Delphi-Versionen sind TThread-Threads intern durch einen "try..except"-Block geschützt. Abgefangene Exceptions werden dann zum Hauptthread geschickt, während der TThread einfach beendet wird. In D5 war es noch so, daß der TThread einfach kommentarlos beendet wurde und die aufgetretene Exception totgeschwiegen wurde. In D4 wurden Exceptions in TThread gar nicht abgefangen, sondern Windows (bzw Dr. Watson) meldete sich dann und wollte das Programm beenden. Threads, die nicht über TThread, sondern direkt über CreateThread erzeugt werden, sind auch in neusten Delphi-Versionen nicht automatisch durch einen "try..except"-Block geschützt. Falls in so einem Thread eine Exception auftritt, meldet sich wiederum Windows/Dr. Watson und möchte das ganze Programm beenden. Was mache ich, wenn mein Programm bei jemand anderem abstürzt - aber nicht bei mir? Die meisten Programmierer kennen wahrscheinlich das Problem: Auf dem eigenen PC läuft das selbst geschriebene Programm problemlos. Aber auf dem PC eines anderen stürzt es ständig ab. Und meistens wohnt dieser andere dann irgendwo am anderen Ende der Welt, so daß eine direkte Untersuchung des Problems vor Ort nicht möglich ist. Was kann man dann tun? Abstürze in Windows-Programmen sind meistens mit Exceptions gleichzusetzen. Das heißt, wenn ein Programm abstürzt, liegt meistens eine Exception vor. Was wir nun brauchen, um den Abstürzen auf den Grund zu gehen, sind mehr (viel mehr) Informationen darüber, was genau passiert ist. Die mit Abstand wichtigste Information ist: An welcher Stelle im Quellcode ist die Exception genau ausgelöst worden? Die zweitwichtigste Information ist wahrscheinlich: Über welchen Weg ist das Programm an diese Stelle gekommen? Beides sind Informationen, die uns Delphi leider nicht gibt. Bei Zugriffsverletzungen (aber auch nur da) meldet uns Delphi eine Code-Adresse, die wir mit etwas Aufwand so interpretieren können, daß wir die Stelle im Quellcode finden, die die Zugriffsverletzung ausgelöst hat. Das klappt allerdings nur, wenn wir genau (100%ig, ohne auch nur die geringste Abweichung) die gleichen Quellen und die gleichen Versionen aller Drittanbieter-Komponenten zur Verfügung haben, mit denen das abgestürzte Programm kompiliert wurde. Und selbst dann ist es schwierig genug, die Stelle im Quellcode zu finden. An dieser Stelle darf ich (mit Erlaubnis der Forum-Verantwortlichen etwas Werbung für meine "madExcept"-Komponente machen. Was ist madExcept und was macht es? madExcept integriert sich automatisch in jedes Delphi-Programm (nur win32, nicht DotNet) und übernimmt die komplette Exception-Verarbeitung von Delphi. Dabei werden folgende Verbesserungen erzielt:
Zuerst einmal muß der Installer her: http://madshi.net/madCollection.exe Nach der Installation von madExcept bitte Delphi starten und das gewünschte Projekt laden. Im Delphis "Projekt"-Menü gibt es jetzt einen neuen Eintrag: Diesen Eintrag bitte anklicken. Es erscheint folgendes Bild: Bitte dort die beiden Haken "handle exceptions" und "append map file to binary" anklicken. Danach das Projekt neu kompilieren - fertig! Wie wirkt sich madExcept zur Laufzeit aus? Erst einmal merkt man keinen Unterschied, da madExcept nur dann eingreift, wenn wirklich eine Exception auftritt. Nehmen wir an, wir drücken auf den "Button1" bei folgendem Code:
Delphi-Quellcode:
Innerhalb der Delphi IDE meldet sich dann erst den Debugger. Bitte mit F9 das Programm weiter laufen lassen. Dann kommt die madExcept Exception-Box. Außerhalb der Delphi IDE kommt sofort die madExcept Exception-Box. Das sieht dann so aus:
procedure TForm1.Button1Click(Sender: TObject);
begin integer(nil^) := 0; end; Wem die englischen Texte nicht gefallen, kann das ganze im (weiter oben schon gezeigten) Einstellungsfenster eindeutschen. Der Endbenutzer kann jetzt die Anwendung fortführen, neu starten oder beenden. Außerdem kann er sich einen detaillierten Fehlerbericht anzeigen lassen oder den Fehlerbericht an den Programmierer mailen. Der Fehlerbericht sieht dann wie folgt aus (stark gekürzte Fassung):
Code:
Den vollständige Fehlerbericht habe ich als Attachment angehängt (siehe bugReport.txt). Die einzelnen Bestandteile des Fehlerberichts lassen sich auf Wunsch auch ausschalten oder nach Wunsch einstellen.
physical memory : 717/1022 MB (free/total)
free disk space : (C:) 68.54 GB allocated memory : 3.44 MB executable : Project1.exe exception class : EAccessViolation exception message : Zugriffsverletzung bei Adresse 00472910 in Modul 'Project1.exe'. Schreiben von Adresse 00000000. main thread ($5d4): 00472910 Project1.exe Unit1 28 TForm1.Button1Click 00471302 Project1.exe Forms TApplication.Run 00472be7 Project1.exe Project1 14 initialization disassembling: 0047290c public TForm1.Button1Click: ; function entry point 0047290c 28 xor eax, eax 0047290e xor edx, edx 00472910 > mov [eax], edx 00472912 29 ret Was kostet madExcept und wo finde ich mehr Informationen? madExcept ist kostenlos für nicht-kommerzielle Zwecke. Für kommerzielle Zwecke kostet es USD 50 pro Programmierer. Die vollständige Dokumentation ist auch online zugänglich, und zwar hier: http://help.madshi.net/madExcept.htm [edit=sakura] Schönheitsfehler beseitigt. Mfg, sakura[/edit] |
Delphi 12 Athens |
#2
Wenn jemand Fragen hierzu hat, so bitte einen neuen Thread aufmachen, welchen ich dann von hier verlinke. Mir ggf. eine kurze PM senden
......
Daniel Lizbeth
|
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |