Session D-FEHL

Errorhandling: Wie man sich sauber aus der Affäre zieht

Jürgen Wondzinski
ProLib Software GmbH


Einleitung

Neulich, in einem schlauen Buch gelesen:
Der Vorteil der objektorientierten Programmierung liegt darin, daß alle verwendeten Objekte schon getestet und fehlerfrei sind. Dadurch sind auch ihre Applikationen fehlerfrei.

Das ist genau so wahr wie:
Die Erde ist eine Scheibe.

Aus eigener Erfahrung wird wohl jeder bestätigen, daß es vermutlich unmöglich ist, eine vollkommen bugfreie Software zu erstellen. Unter bestimmten Umständen wird jede Software Fehler aufzeigen. Aber es ist unser Job als Programmierer, dafür zu sorgen, daß diese Fehlerbedingungen so elegant wie möglich behandelt werden.

In der prozeduralen Vergangenheit haben wir nur den ON ERROR Befehl gehabt, und auf der Basis eines globalen Error-Handlers versucht, mit allen auftretenden Fehlern fertig zu werden. Dazu wurden ellenlange DO CASE Anweisungen aufgebaut, die versucht haben, jeden möglichen Fehler zu klassifizieren und entsprechend zu reagieren. Da dies aber alles von einer zentralen Stelle aus passieren mußte, blieb meist nicht viel mehr übrig, als den Fehler in eine Logdatei zu schreiben, und dann möglichst unauffällig das Programm zu beenden und neu anzustarten.....

Aber in der schönen neuen objektorientierten Welt von VFP haben wir ja nun wesentlich mehr Möglichkeiten!

Aufgaben des Error-Handlers:

Vielleicht sollten wir erstmal überlegen, was eigentlich unser Ziel ist. Obwohl man auf den ersten Blick sagen möchte: "na klar, ein Errorhandler soll die Fehler bearbeiten", ist es doch nicht ganz so einfach. Obwohl dies ein erstrebenswertes Ziel ist, so sollte es trotzdem nicht die Hauptaufgabe sein. Vielmehr haben wir es eigentlich mit mehreren Aufgaben zu tun:

Unsere eingegebenen Daten retten und protokollieren, welcher Fehler aufgetreten ist, und warum es dazu kommen konnte. Das ganze so ausführlich wie möglich, so daß wir als Programmierer die Situation nachvollziehen können.

Wann ist ein Fehler ein Fehler?

Was halten Sie davon:
"Programme können keine Fehler bekommen - der Programmierer hat sie verursacht"

Wenn man mal darüber nachdenkt, wird einem klar, daß die meisten Fehler erst durch uns Programmierer entstehen. Fehler sind nicht einfach da, sondern werden erzeugt. Entweder haben wir etwas falsch gemacht, oder wir haben weitere Programmschritte zugelassen, ohne uns vorher vergewissert zu haben, daß alle Voraussetzungen vorhanden sind.

Im Normalfall können wir Fehler in verschiedene Kategorien einteilen:

  • Programmierung
  • Hardware Probleme
  • Konflikte
  • Schweinereien

Programmfehler: Das sind die üblichen Sachen wie "Fehlende Parameter", "Falscher Datentyp", "Programm nicht gefunden" oder schlicht Schreibfehler, die wir ja alle noch vor der Auslieferung durch intensives Testen beseitigen....

Hardwarefehler treten immer nur beim Kunden auf. Möglicherweise hat er eine andere Windows-Version, ein anderes Betriebssystem, andere Speicherkonfiguration, Plattengröße usw. Theoretisch sollten diese Fehler schon bei der Installation auf dem Kundenrechner in den Griff zu kriegen sein.

Die nächste Quelle sind die Konflikte, die eigentlich keine Fehler im klassischen Sinne sind. "Datei nicht gefunden" ist der klassische Vertreter dieser Gattung. Es ist eigentlich kein Fehler, es ist eine Situation, die wir besser vorher abgeprüft hätten, bevor es soweit kommt.

Bleiben nur noch die Schweinereien übrig, die echten Fehlersituationen in einer laufenden Applikation. Schweinereien sind das, wo Sie beim Auftreten derselben laut zu fluchen beginnen... z.B. einige Programmierfehler, die trotz extensiven Testen ausgerechnet beim Jahresabschluß auftreten. Tatsächlich aber, wenn wir die ersten drei Gruppen berücksichtigen, bleiben nicht mehr viele echte "Fehler" übrig, um die wir uns kümmern müssen. Übrig bleiben z.B. so Sachen wie "Fehler 56: Nicht genug Plattenplatz" oder "Kann nicht von Laufwerk C: lesen". Sachen, denen man eigentlich machtlos gegenübersteht, obwohl sie vielleicht auch zur Gattung "Konflikte" gehören.

Auf jeden Fall aber müssen wir unsere Fehlerbehandlung auf alle diese Ereignisse ausrichten.

Befehle und Funktionen

Bevor wir uns überhaupt über die Implementierung unserer Routine Gedanken machen, sollten wir uns mal über alle zur Verfügung stehenden Möglichkeiten informieren: Hier erst mal die klassischen Fehler-Einfang-Hilfsmittel:

ON ERROR

Gibt einen Befehl an, der beim Auftreten eines Fehlers ausgeführt wird. Nachdem der Befehl ausgeführt wurde, wird die Programmausführung unmittelbar nach der Zeile fortgesetzt, die den Fehler verursachte. Wenn die Fehlerbehandlungsprozedur jedoch RETRY enthält, wird die Programmzeile mit dem Fehler erneut ausgeführt.

Verwenden Sie ON ERROR ohne einen Befehl, um die standardmäßige FoxPro-Fehlerbehandlungsroutine erneut zu laden.

ERROR()

Gibt die Nummer des Fehlers zurück, der eine ON ERROR-Routine ausgelöst hat. Der Rückgabewert von ERROR() wird mit RETURN oder RETRY zurückgesetzt.

MESSAGE()

Gibt die aktuelle Fehlermeldung als Zeichenfolge zurück. Im Gegensatz zu ERROR() wird MESSAGE() von RETURN oder RETRY nicht zurückgesetzt.

MESSAGE(1)

Gibt den Inhalt der Programmzeile zurück, die den Fehler verursachte. Wenn das Programm mit der Option "Keine Debuginformationen" kompiliert worden ist, gibt MESSAGE(1) eines der folgenden Elemente zurück:

  • Die gesamte Programmzeile, wenn die Zeile aus einer Makrosubstitution resultiert.
  • Das Befehlswort, wenn die Zeile einen Befehl ohne zusätzliche Klauseln enthält.
  • Das Befehlswort gefolgt von drei Punkten (...), wenn die Zeile einen Befehl und zusätzliche Klauseln enthält.

LINENO()

Gibt die Zeilennummer der Zeile zurück, die gerade ausgeführt wird. Es wird relativ zur ersten Zeile des Hauptprogramms gemessen.

LINENO(1)

Gibt die Zeilennummer relativ zur ersten Zeile des aktuellen Programms bzw. der aktuellen Prozedur zurück. Dies kann einen Unterschied in Prozedurdateien machen.

SYS(2018)

Bestimmte Fehlermeldungen geben zusätzliche Informationen über den Verursacher des Fehlers zurück. Wenn Sie zum Beispiel auf eine Speichervariable verweisen, die nicht vorhanden ist, ist der Name der Speichervariablen in der Fehlermeldung enthalten. SYS(2018) gibt diese zusätzlichen Informationen, die auch als Fehlermeldungsparameter bezeichnet werden, zurück.

RETRY

RETRY gibt die Kontrolle an das aufrufende Programm zurück und führt die letzte in dem betreffenden Programm ausgeführte Zeile erneut aus. RETRY ist ähnlich wie RETURN. Der Unterschied liegt darin, daß bei RETURN die nächste Zeile im aufrufenden Programm ausgeführt wird.

RETURN [TO <Programm>| MASTER]

Gibt die Programmkontrolle an das aufrufende Programm zurück.

TO Prozedurname

    Gibt die Prozedur bzw Methode an, an die die Kontrolle zurückgegeben wird.

TO MASTER

    Gibt die Kontrolle an das oberste aufrufende Programm zurück.

Neue Möglichkeiten in Visual FoxPro

Während die alten FP2.x Befehle nicht erweitert wurden, haben wir mit VFP drei neue Dinge bekommen: die .ERROR Methode, den ERROR Befehl und die AERROR() Funktion.

.ERROR Methode

Alle VFP Objekte haben eine eigene ERROR Methode, die nun im Fehlerfalle als erstes angesprochen wird. Wenn weder in dieser Methode noch weiter oben in der Klassenhierarchie ein Code gefunden werden kann, so wird eine globale ON ERROR Funktion wieder aktiviert. Oder anders ausgedrückt: Die globale ON EROR Methode verliert ihre Gültigkeit, wenn ein Objekt eine eigene .ERROR Methode definiert hat.

LPARAMETERS [nIndex,] nFehler, zMethode, nZeile

Visual FoxPro übergibt dem Error-Ereignis drei bis vier Parameter in der folgenden Reihenfolge.

nIndex

    Optional, Wird nur übergeben, wenn das Objekt Teil eines Objekt-Arrays ist

nFehler

    Enthält die Visual FoxPro-Fehlernummer.

zMethode

    Enthält den Namen der Methode, die den Fehler verursacht hat. Wenn jedoch eine Methode eine benutzerdefinierte Funktion aufruft und in dieser Funktion ein Fehler auftritt, enthält zMethode den Namen der benutzerdefinierten Funktion und nicht den Namen der Methode, die diese Funktion aufgerufen hat.

nZeile

    Enthält die Nummer der Zeile der Methode oder benutzerdefinierten Funktion, in der der Fehler aufgetreten ist.

Mit Hilfe des Error-Ereignisses kann ein Objekt Fehler abfangen. Das Ereignis setzt die aktuelle ON ERROR-Routine außer Kraft, so daß jedes Objekt Fehler intern abfangen und Fehlerbehandlungsaktionen ausführen kann.

Wichtig: Das Error-Ereignis wird nur aufgerufen, wenn der Fehler im Code auftritt. Bei Properties wird kein Code ausgeführt, folglich auch kein ERROR Handler.

Noch wichtiger: Ist FoxPro erstmal im Fehlermodus (sprich beim Abarbeiten von ON ERROR bzw. ERROR ,Methode), dann kann kein zweiter Errorhandler aktiv sein. Falls also im Errorhandler nochmals ein Fehler auftritt, so erscheint gnadenlos die FoxPro interne Fehlermessagebox.

ERROR Befehl

nFehlernummer | nFehlernummer, zMeldungstext1 | [zMeldungstext2]

Mit Hilfe von ERROR können Sie ihre Fehlerbehandlungsroutinen testen oder benutzerdefinierte Fehlermeldungen anzeigen lassen. Von Seiten FoxPro's gibt es keinen Unterschied ob ein "echter" Fehler aufgetreten ist, oder sie ihn mit diesem Befehl erzeugt haben. Eigentlich hat dieser Befehl nichts mit dem Fehlerabfang zu tun, außer sie sorgen damit, daß es Ihren Kunden nie langweilig wird...

nFehlernummer

    Gibt die Nummer des zu erzeugenden Fehlers an. Wenn Sie eine Fehlernummer angeben, wird die entsprechende Visual FoxPro-Standardfehlermeldung ausgegeben.

zMeldungstext1

    Gibt den Text an, der als Fehlermeldung angezeigt wird und zusätzliche Informationen über den Fehler liefert. Wenn Sie z.B. eine nicht vorhandene Speichervariable ansprechen, gibt Visual FoxPro den Namen der Speichervariablen in der Fehlermeldung aus.

zMeldungstext2

    Gibt den Text an, der als Fehlermeldung angezeigt wird. Wenn Sie zMeldungstext2 anstelle von nFehlernummer angeben, wird der Visual FoxPro-Fehler 1098 (benutzerdefinierter Fehler) erzeugt. Fügen Sie ein CHR(13) in zMeldungstext2 ein, wenn Sie einen Teil des Textes in die nächste Zeile verschieben möchten.

Ist eine ON ERROR-Fehlerbehandlungsroutine in Kraft, wenn ERROR eingegeben wird, führt Visual FoxPro die ON ERROR-Routine aus. Tritt ein auf ein Objekt bezogener Fehler auf, wird das Error-Ereignis des Objekts ausgeführt.

Wenn Sie ERROR über das Befehlsfenster ausgeben und keine ON ERROR-Fehlerbehandlungsroutine in Kraft ist, gibt Visual FoxPro die Fehlermeldung aus. Wenn ERROR in einem Programm eingegeben wird und keine ON ERROR-Fehlerbehandlungsroutine in Kraft ist, gibt Visual FoxPro die Fehlermeldung aus und ermöglicht es Ihnen, das Programm abzubrechen, zu unterbrechen oder den Fehler zu ignorieren.

AERROR() Funktion

Erstellt ein Array, das Informationen zu dem zuletzt aufgetretenen Visual FoxPro-, OLE- oder ODBC-Fehler enthält.

AERROR( ) erstellt in VFP3 ein Array mit sieben Spalten und gibt die Anzahl der Zeilen des Arrays zurück. Der Typ des aufgetretenen Fehlers bestimmt, wie viele Zeilen das Datenfeld hat. Normale VFP Fehler erstellen eine Zeile, wohingegen OLE Fehler mehrere Zeilen erzeugen. Im Normalfall stehen folgende Informationen drin:

Zeile Inhalt

1

Numerisch. Die Nummer des Fehlers. Ist identisch mit dem von ERROR( ) zurückgegebenen Wert.

2

Zeichen. Der Text der Fehlermeldung. Ist identisch mit dem von MESSAGE( ) zurückgegebenen Text.

3

Nullwert (.NULL.). Enthält allerdings für den Fall, daß der Fehler einen zusätzlichen Fehlerparameter hat, den Text des Fehlerparameters. Ist identisch mit dem von SYS(2018) zurückgegebenen Text.

4

Nullwert (.NULL.). Enthält aber gegebenenfalls die Nummer des Arbeitsbereichs, in dem der Fehler auftrat.

5

Nullwert (.NULL.). Enthält allerdings für den Fall, daß ein Trigger versagt hat (Fehler 1539), eine der folgenden Zahlen:

1 - INSERT-Trigger hat versagt.

2 - UPDATE-Trigger hat versagt

3 - DELETE-Trigger hat versagt

6

Nullwert (.NULL.).

7

Nullwert (.NULL.).

Im Prinzip steht also nicht mehr drin, als wir bisher schon über die Einzelfunktionen kannten. Interessant wird's erst, wenn wir zu den gemeinen Fehlern kommen: OLE und ODBC Fehler. Unter den allgemeinen FoxPro Fehlernummern 1426, 1427 und 1526 verbergen sich ja externe Fehlerquellen, an denen FoxPro nicht viel reparieren kann. Bei diesen Fehlern werden immer mind. zweizeilige Arrays zurückgegeben, je nach Anzahl der auftretenden Fehlernachrichten des Servers.

OLE-Fehler 1426 oder 1427

Zeile Inhalt

1

Numerisch. Enthält 1426 oder 1427.

2

Zeichen. Der Text der Visual FoxPro-Fehlermeldung.

3

Zeichen. Der Text der OLE-Fehlermeldung.

4

Zeichen. Der Name der Anwendung (z.B. Microsoft Excel).

5

Nullwert (.NULL.) oder Zeichen. Enthält den Namen der Hilfedatei der Anwendung, in der Sie weitere Informationen zu dem Fehler finden können, sofern diese Informationen von der Anwendung bereitgestellt werden. Enthält andernfalls den Nullwert.

6

Nullwert (.NULL.) oder Zeichen. Enthält die Hilfekontextkennung des entsprechenden Hilfethemas, sofern diese Informationen von der Anwendung bereitgestellt werden. Enthält andernfalls den Nullwert.

7

Numerisch. Die Nummer einer OLE 2.0-Ausnahme.

ODBC-Fehler 1526

Zeile Inhalt

1

Numerisch. Enthält 1526.

2

Zeichen. Der Text der Fehlermeldung.

3

Zeichen. Der Text der ODBC-Fehlermeldung.

4

Zeichen. Der aktuelle ODBC SQL-Status.

5

Numerisch. Die Fehlernummer der ODBC-Datenquelle.

6

Numerisch. Die ODBC-Verbindungskennung.

7

Nullwert (.NULL.)

Error-Handler Design

Wir können es sicherlich als gegeben ansehen, daß es nie das fehlerfreie Programm geben wird. Aber wir sollten soweit in der Lage sein, daß unsere Anwender nicht bei jedem Programmstart mit Fehlermeldungen überschüttet werden. Wenn der Anwender eine Fehlermeldung sieht, dann hat der Programmierer seine Arbeit nicht ordentlich erledigt. O.k., das klingt übertrieben, aber es ist die Wahrheit.

Um sicherzugehen, daß der Anwender keine Fehlermeldungen zu sehen bekommt, stehen dem Entwickler zwei Möglichkeiten offen: Entweder hat der Errorhandler stabil genug zu sein, um alles abzufangen, oder der Programmierer muß bei jedem Programmschritt alle Möglichkeiten abprüfen.

In der Vergangenheit hat man meist versucht, alle möglichen Fehler zentral abzufangen (siehe z.B. Pat Adam's ProError.prg),. Bedingt durch den ON ERROR Befehl war es auch normalerweise gar nicht anders möglich, als ein riesengroßes DO CASE Statement zusammenzubauen, das alle möglichen Fehler sauber auflistete. Manche Fehler konnte man so zwar sauber bereinigen, aber bei vielen anderen blieb nur ein möglichst elegantes Beenden der Applikation übrig. Dies alles hat wohl in den FoxPro 2.x Version vollkommen genügt, aber in Visual FoxPro haben sich die Anforderungen doch entscheidend geändert: Mehrfache Instanzen einer Form, Private Datasessions, DataBuffering, OLE und Client/Server Verbindungen sind die Hauptursachen dafür. Diese so radikal geänderten Abläufe innerhalb FoxPro's führen zwangsläufig zu der Erkenntnis, daß man a) es überhaupt nicht zu einen Fehlerfall kommen lassen sollte, und b) die Fehlerbehandlung so nah wie möglich am Verursacher vornehmen muß. Das Objekt, das den Fehler verursacht, weiß sicherlich mehr über die Umstände als ein zentraler Errorhandler je wissen kann.

Der einzig wahre Weg, unter VFP die Fehler zu bekämpfen, ist so nah wie möglich an die Ursache ranzukommen. Je näher wir dran sind, desto mehr wissen wir über die Ursachen. Das bedeutet, daß das verursachende Objekt versuchen muß, seine spezifischen Fehler selbst zu beseitigen, und allgemeine Fehler an den Hintergrund weitergibt.

Weitergabe innerhalb Klassen

Wir können sicherlich davon ausgehen, daß eine Subklasse immer eine auf spezielle Probleme angepaßte Version der Elternklasse ist. Folglich weiß ein Subklasse auch mehr über die zu erwartenden Fehler. Dies gibt uns schon mal recht gute Ansätze zur Bugbekämpfung.

Wenn ein Fehler auftritt, so hat FoxPro ersteinmal das Control zu überprüfen, in dem der Fehler sich ereignete. Der Error feuert also die .ERROR Methode des aktuellen Objektes. Ist hier tatsächlich Code enthalten, wird dieser ausgeführt, ansonsten überprüft FoxPro nun die Klassenhierarchie. Findet es in der gesamten Vererbungsstruktur keinen Code, so wird der normale ON ERROR Code ausgeführt. Ist auch dieser nicht zugewiesen, so erscheint die übliche FoxPro interne Fehlermessage.

Wenn man diesen Ablauf so sieht, macht es Sinn, den entsprechenden Fehlercode entweder in das Control selbst, oder in eine der übergeordneten Klassen zu packen. Das Problem dabei ist, daß wir auf diese Weise wieder nicht gezielt einzelne Anpassungen an spezielle Gegebenheiten machen können, und wir dadurch weniger flexibel werden. Speziell wenn man verschachtelte Objekte als fertige Klassen verwendet, wird man feststellen, daß der gewohnte Aufruf der Elternmethoden mittels des :: Operators sich nicht an die eigentliche Vererbung hält, sondern quasi eine Abkürzung zum Grundobjekt nimmt.

Mit der VFP5 haben wir daher eine neue Funktion bekommen: DoDefault(), die ausgehend von einer Subklasse, immer den Methodencode der Elternklasse ausführt. In unserem Fall führt also DoDefault() den ERROR Code der Elternklasse aus. Das ganze sieht man am besten, wenn Sie die Beispieldateien betrachten: ETEST.SCX, EFIRST.VCX und EBASE.VCX .

ETEST.SCX enthält nur einen einzelnen Button, dessen CLICK einen Fehler verursacht. Da die Form selbst keinen Code beinhaltet, sucht FoxPro nun in der Instanz der Formklasse, ob es einen Errorhandler findet. Dieser Errorhandler besteht nun aus einem DO CASE , das ihm bekannte Fehler ausfiltert, und den Rest im OTHERWISE via DoDefault() an die nächsthöhere Instanz weiterleitet. Dies ist nun die Schaltfläche in der EFIRST.VCX. Auch hier ist im Error-Event ein ähnliches DO CASE Konstrukt integriert, das wiederum via DoDefault() die Schaltfläche in EBASE.VCX aufruft. Wenn sie diese Form laufen lassen, werden Sie eine Reihe von WAIT WINDOW Nachrichten sehen, die Ihnen genau über den Ablauf Bescheid geben.

Im wirklichen Leben würde diese DO CASE Anweisung sicherlich etwas Code enthalten, um mit spezifischen Fehlern klarzukommen. Durch die Klassentechnologie können wir auch Error-Events vererben als auch überschreiben, bzw. erweitern.

Solange man sich dran erinnert, daß FoxPro immer jeweils in die übergeordnete Klasse schaut, ob entsprechender Code vorhanden ist, und diesen dann startet, solange bis es keinen Code mehr findet. Jede der angestarteten Klassen beinhaltet wieder eine DO CASE Struktur, anhand dessen entschieden wird, ob diese Klasse mit dem Fehler etwas anfangen kann. Wir können dies also wunderbar dafür verwenden, um von einer sehr spezialisierten Fehlerbehandlung zu einer immer globaleren Fehlerbehandlung zu kommen.

Irgendwann sind wir dann in unseren Basisklassen angekommen (hier EBASE.VCX), und wir müssen uns fragen, was wir nun mit unserem Fehler anfangen. Ganz einfach: Weiterhin nach einem Dummen suchen, dem man den Job übergeben kann.

Weitergabe innerhalb Containern

Der Trick ist nun, nach dem Passieren der einzelnen Objekte, auf den einbettenden Container zu wechseln, und zu hoffen, daß dieser vielleicht den Fehler beseitigen kann. Auch hier kann man nun bis zur entsprechenden Basisklasse hochrobben. Wer dann noch immer seinen Fehler nicht losgeworden ist, der bekommt nun den normalen Errorhandler von FoxPro zu sehen.

Wir testen also zuerst die Pageframe, auf der der Button sich befindet, danach die Form. Auf dem ETEST.SCX kann man so mit entsprechenden WAIT WINDOWS das ganze mal im Eigenversuch selbst durchspielen.

Es gibt nur wieder mal eine kleine Ausnahme: In einem Grid sollte man die Fehlerbehandlung nicht in den einzelnen Textboxen, Headern und Columns horten, sondern gesammelt im GRID Errorhandler reinsetzen.

Probleme, Probleme..

Das hier vorgestellte Prinzip des Problem-Weiterreichens von einem spezialisierten Objekt bis hin zum allgemeingültigen Standard-Errorhandlers sorgt dafür, daß nur dort ein Errorhandler-Code eingetragen werden muß, wo es notwendig ist.

Trotzdem bleiben einige Probleme zu beachten:

  1. Bei der Verwendung von Objekt-Collections wie z.B. Pageframes, Schaltflächen-Gruppen, Optionsgruppen und Formsets, werden nur die original vorhandenen Controls mit ihrem Code weitervererbt. Wenn Sie z.B. den PageCount einer gesubclassten Pageframe höhersetzen, so ist in den neuen Seiten der Errorcode nicht enthalten. Das heißt: hier müssen sie den Code jedesmal selbst reinschreiben.
  2. Solange FoxPro in einem Fehlerzustand ist, kann kein weiterer Errorhandler angestartet werden. FoxPro kann nur einen Errorhandler verwalten. Dies bedeutet aber auch, daß wir die Standard Fehlerbox erhalten, wenn innerhalb unseres angestarteten Fehlerprogramms nochmals ein Fehler auftritt.
  3. Der DoDefault() hat in der VFP5 Version, Probleme mit dem richtigen Aufrufen der einzelnen Codeteile, wenn man innerhalb der Vererbung eine Methodenebene leer läßt; dieses Problem ist aber in VFP6 beseitigt. Die Beispielmaske DoDe.scx zeigt dies anschaulich: Der Aufruf aus Stufe zwei wird zweimal ausgeführt, da in der Stufe drei kein Aufruf vorhanden war. Es scheint, als ob FoxPro sich intern merkt, wieviele Vererbungsstufen zurückliegen, und dann partout diese Anzahl zurückgehen will. Abhilfe schafft hier nur die Merkregel: Wenn DoDefault(), dann in allen übergeordneten Ebenen ebenfalls.

Tips und Tricks

Fehlerfreie Programme, ganz einfach...

Wollen Sie mal ihre Kollegen ärgern, so setzen Sie einfach die Zeile

ON ERROR *

in ihr Programm, und sie werden nie wieder von einem Fehler belästigt. ON ERROR * ist etwas anderes als ein reines ON ERROR: Ohne Anweisung wird ja auf den FoxPro eigenen Default zurückgeschaltet, mit angegebener Anweisung diese eben ausgeführt. Dummerweise ist nun die Anweisung "*" eben nur ein Kommentar....

InterTask Messaging

Durch die ERROR Befehlsoption, anstelle der Fehlernummer einen eigenen Messagetext anzugeben, kann man sich ein automatisches Messagesystem aufbauen, ohne langwierig mit einem Timerobjekt auf Nachrichten zu horchen, oder Kenntnis von anderen Objekten zu haben. Sobald man also einen beliebigen String mit ERROR abschickt, wird im aktiven Errorhandler die Fehlernummer 1098 generiert, und man kann dort den String analysieren und diverse Aktionen auslösen.

ERROR "Hallo"

erzeugt die Fehlernummer 1098, und die MESSAGE() Funktion enthält die Nachricht.

Eine nicht unbedingt täglich benötigte Idee, ab und an aber ganz nützlich. z.B. könnte man damit allen Objekten mitteilen, daß sich ein Datensatz einer Tabelle geändert hat, und alle Objekte, die zufällig diesen Datensatz anzeigen, könnten ihn nun neu einlesen.

Genaue Triggerfehler Analyse

Bei einem Triggerfehler (1539) erhält man in der Regel keinerlei genaue Angaben, was denn nun zu dem Fehler geführt hat. Welches Feld, welcher Trigger hat denn nun nicht geklappt? Das von aError() erstellte Array gibt uns da leider nicht sehr viel Auskunft: In Spalte 5 ist zwar angegeben, welcher Trigger (Update/Insert/Delete) ein Problem hatte, aber nirgends steht, warum..... Gottseidank wird aber vom Triggercode selbst ein Public Array angelegt, das dann exakte Informationen liefert:

gaErrors[FehlerEbene, 1] = Interne RI Fehlernummer
gaErrors[FehlerEbene, 2] = Fehlertext
gaErrors[FehlerEbene, 3] = Zeile
gaErrors[FehlerEbene, 4] = Aufrufhierarchie, durch Kommas getrennt
 
gaErrors[FehlerEbene, 5] = Vater Tabellenname
gaErrors[FehlerEbene, 6] = Vater Satznummer
gaErrors[FehlerEbene, 7] = Vater Schlüsselwert
gaErrors[FehlerEbene, 8] = Vater Indexausdruck
 
gaErrors[FehlerEbene, 9] = Kind Tabellenname
gaErrors[FehlerEbene,10] = Kind Satznummer
gaErrors[FehlerEbene,11] = Kind Schlüsselwert
gaErrors[FehlerEbene,12] = Kind Indexausdruck

Die erste Spalte ist abhängig von der Anzahl der aufgetretenen Triggerfehler, d.h. zwei Triggerfehler hintereinander aufgetreten, dann zwei Ebenen. Die zweite Spalte beinhaltet dazu die aktuellen Fehlerinfos.

Gruselgeschichte:

Mit folgendem Code können Sie sich mal so richtig vor dem zu Bett gehen gruseln: Alle Fehlerquellen, die FoxPro kennt:

CLEAR ALL
RELEASE ALL
CREATE TABLE ERRORS (nError I, cMessage C(200))
 
ON ERROR DO WriteIt  WITH ERROR(), MESSAGE()
 
FOR nFehler = 1 TO 10000
   ERROR nFehler
ENDFOR
ON ERROR
 
BROWSE
RETURN
 
**************************************************
PROCEDURE WriteIt
Parameter tnError, tcMessage
 
IF NOT tnError = 1941   && errorcode not valid
   INSERT INTO Errors VALUES(nFehler, tcMessage)
ENDIF
 
RETURN

Nun noch schnell einen Bericht über diese Tabelle und fertig ist die Gute-Nacht-Geschichte!

Neue Hardware für den Kunden...

Ein Timerobjekt, das zu unregelmäßigen Zeiten ein paar Fehlernummern mittels ERROR anstartet, kann sehr effizient zu neuen Festplatteninstallationen führen...

Wie wär's z.B. mit Nummer 1104: "Fehler beim Lesen der Datei." ? Oder damit:

ERROR "Rechner zu langsam für Ihre Daten."

Ein bißchen Kreativität kann ja nicht schaden....