Eigentlich gibt es in einer LS-Applikation 3 Sorten von Fehlern, die während der Laufzeit auftreten können:
1. Fehler, welche von LotusScript entdeckt und mit einer Fehlermeldung versehen werden. Wenn man gegen solche Fehler nichts unternimmt, bekommt der Benutzer eine Fehlermeldung und das Script bricht ab. Beispiel:
erzeugt einen Fehler, weil die Division durch 0 nicht erlaubt ist.
2. Fehlerzustände, die LotusScript nicht eindeutig als solche erkennen kann, auf die das Programm dann entweder selber reagieren muss, oder es führt dann später im Code zu einer entsprechenden Fehlfunktion des Programmes, weil die Umstände nicht so waren wie erwartet. Beispiel:
Dim db As NotesDatabase
Call db.Open( "HongKong", "sales.nsf"
Wenn auf dem Server "HonKong" die Datenbank "sales.nsf" nicht existiert, passiert hier mal gar nichts. Erst wenn ich zum Beispiel mit db.GetDociumentById(..), erhalte ich die Fehlermeldung, dass die Datenbank nicht offen ist.
3. Dann gibt es natürlich auch noch die logischen Fehler. Das Programm läuft zwar korrekt ab, erzeugt weder Fehlermeldungen noch werden "illegale Zustände" wie unter 2. erzeugt, aber trotzdem ist das Ergebnis nicht das, was man erwartet. Entweder stimmt die Applikationslogik nicht, oder das Programm ist nicht nach den Spezifikationen umgesetzt worden. Natürlich können wir hier nicht über diese Probleme reden, da diese ganz individuell angegangen werden können. Was wir hier diskutieren können, sind Taktiken, die man anwenden kann, um diese Probleme einzugrenzen und zu lösen.
Die oben aufgeführten einzelnen Fehlerkategorien erfordern natürlich unterschiedliche Ansätze, um sie zu "behandeln":
1. Von LotusScript erkannte und behandelte Fehler
Da in diesem Fall ein Abbruch des Programmes erfolgt, ist dies eigenlich für den Anwender eine Katastrophensituation, die man wenn immer möglich vermeiden sollte. LotusScript stellt dafür eine ganze Reihe von Statements zur Verfügung, die einem ermöglichen, die "Situation" zu retten. Im Vordergrund stett das On Error Statement. Diese Fehlerbehandlungs-Mechanismen werden in den nächsten Postings im einzelnen noch vorgestellt.
2. Fehlersituationen, die von LotusScript nicht mit einem "Fehler belohnt" werden
Hier gibt es verschiedene Strategien:
A Gar nichts machen und warten, bis irgendwann einmal später die Situation 1. eintrifft und ein Fehler generiert wird, der abgehandelt werden kann.
B In Abhängigkeit von der Situation versuchen, festzustellen, ob das Statement den gewünschten Erfolg gebracht hat. Im Fall von NotesDatabase.Open gibt es da verschiedene Varianten:
Diese Methode hat einen Rückgabewert: True, wenn die Datenbank geöffnet werden konnte, andernfalls False, wir können also den Rückgabewert auswerten:
Dim db As NotesDatabase
If Not db.Open( "HongKong", "sales.nsf" Then
Messagebox "Die Datenbank konnte nicht geöffnet werden"
Else
........
End If
Alternativ kann die Eigenschaft IsOpen abgefragt werden:
Dim db As NotesDatabase
Call db.Open( "HongKong", "sales.nsf"
If Not db.IsOpen Then
Messagebox "Die Datenbank konnte nicht geöffnet werden"
Else
........
End If
Die Designer-Hilfe ist dabei zu Rate zu ziehen, bei jedem Befehl, bei jeder Methode und Funktion ist jeweils angegeben, was passiert, wenn nicht das erwartete Resultat erreicht wird. Insbesondere ist zu beachten, dass es grundsätzlich keine Systematik vorhanden ist, wann Methode 1 mit der Fehlermeldung und wann Methode 2 über einen Status-Returnwert oder eine Status-Abfrage oder ähnliches zur Anwendung gelangen muss.
Das On Error Statement verändert das Verhalten von LotusScript. Sobald dieses Statement ausgeführt wurde, wird keine Fehlermeldung generiert, sondern eine vom Programmierer definierte Aktion ausgeführt.
1. On Error ohne Fehlernummer
Diese Variante von On Error kann in 3 verschiedenen Formen erscheinen:
a. On Error Goto Sprungadresse
In diesem Falle wird der Code ausgeführt, der hinter der Sprungadresse (Label) aufgeführt wird. Dieser Code muss mit einer Form von Resume abgeschlossen werden, damit LotusScript weiss, dass der Fehler jetzt behandelt ist. (Das besagt nciht zwingend, dass der Fehler dadurch behoben oder beseitigt wurde!).
Beispiel:
Sub Demo
On Error Goto ErrHandler
...... hier steht der Programmcode, bei einem Fehler wird statt einer Fehlermeldung
der unten aufgeführte Code ausgeführt
.....
Exit Sub
ErrHandler:
Print "Es ist ein Fehler aufgetreten" ' Hey, hier sollte sinnvoller Code stehen :-)
Resume
b. On Error Goto 0
Mit diesem Statement wird eine besondere Fehlerbehandlungsroutine wieder zurückgesetzt. Es hängt jetzt davon ab, ob meherere On Error-Statements definiert waren oder nicht. Dieses Statement setzt zurück auf den Zustand, der vor dem zuletzt ausgeführten On Error Statement existierte. Allenfalls wird dadurch wieder das LotusScript Standardverfahren hergestellt. Auch wenn On Error Goto 0 nicht verwendet wird, werden alle On Error-Statements deaktiviert, sobald man an das Ende eines Moduls (sub oder function oder Methode) erreicht hat und das Modul verlässt. Beispiel unter c.
c. On Error Resume Next
Eine der gefährlichsten Konstruktionen überhaupt, die Fehlermeldung wird ganz einfach unterdrückt und das Programm mit dem nächsten Statement fortgesetzt. Auf diesem Weg werden Fehler vom Typ 1. zu Fehlern vom Typ 2., die anschliessend eine Ueberprüfung des Zustandes erfordern.
Dim dir As NotesDbDirectory
Dim db As NotesDatabase
Set dir = s.GetDbDirectory("")
On Error Resume Next
Set db = dir.CreateDatabase("quack.nsf", True)
On Error Goto 0
If db is nothing then
Messagebox "Datenbank existiert bereits"
End if
2. On Error mit Fehlernummer
Alle drei oben beschriebenen Varianten können mit einer Fehlernummer versehen werden. Während bei obigen Beispielen immer alle Fehlermeldungen unterdrückt und speziell behandelt werden, lässt sich mit Angabe einer Fehlernummer dieses Verfahren auf genau eine einzige Fehlermeldung beschränken. Beispiel:
Sub Demo
On Error 11 goto DivByZero
Resultat = Var1 / 0
On Error Goto 0
Exit Sub
DivByZero:
Messagebox "Division durch 0 aufgetreten"
Resume Next
End Sub
Die Fehlernummern findet man in der Designerhilfe. Ausserdem kann man statt mit Nummern mit "sprechenden" Konstanten arbeiten, wenn man die folgenden Dateien mit HIlfe von %include in sein Programm integriert:
lsxbeerr.lss defines constants for errors raised by Domino Designer back-end methods.
lsxuierr.lss defines constants for errors raised by Domino Designer front-end (UI) methods.
lserr.lss defines constants for errors raised by LotusScript.
lsconst.lss defines constants for use as arguments in LotusScript statements and functions.
Resume schliesst eine Fehlerbehandlungsroutine ab und teilt somit LotusScript mit, dass der Fehler nicht mehr als solcher zu betrachten ist (technisch gemeint natürlich)
Das Statement kann in 4 Formen auftreten:
Resume
Resume 0
Resume Next
Resume Sprungadresse
Resume und Resume 0 sind gleichbedeutend:
Mit diesen Befehlen wird das Statement, das den Fehler verurstachte, noch einmal ausgeführt: Achtung: das darf natürlich nur gemacht werden, wenn in der Fehlerbehandlungsroutine der Grund für den Fehler beseitigt wurde, also beispielsweise nach einem neuen Dateinamen gefragt wurde. Sonst passiert der Fehler ja gleich noch einmal und wir landen gleich wieder am selben Ort ....... und haben eine Endlosschleife ...... (Hinweis für TMC: Das wäre vielleicht als Beispiel in die Weihnachsschleifen-Sammlung aufzunehmen)
Mit Resume Next wird die Programmausführung beim nächsten Befehl hinter dem Statement, welches den Fehler auslöste, fortgesetzt. Auch hier gilt: im Fehlerbehandlungsprgramm sollte dafür gesorgt werden, dass das Programm weitergeführt werden kann. Hier ist es allerdings nicht so tragisch wie oben, das Programm wird ja einen Schritt nach der Fehlerstelle weitergeführt. Also auch wenn ein weiterer Fehler auftritt und die Routine wieder aufgerufen wird, kommen wir trotzdem im Programmablauf vorwärts. Vorsicht ist natürlich angebracht.
Resume Sprungadresse
Hier können wir das Programm an einem beliebigen Label fortsetzen, damit Teile, die sonst durch den Fehler nicht laufen würden, überspringen.
Beispiel:
Sub Demo
On Error Goto ErrHandler
... hier der Code
AfterError:
Print "Routine Demo beendet"
Exit Sub
ErrHandler:
Print "Es ist ein Fehler aufgetreten"
Resume AfterError:
End Sub
Da wir ja jetzt mit On Error die Fehlermeldungen unterdrücken können, müssen wir in unserer Fehlerbehandlungsroutine feststellen können, was denn nun passiert ist. Dafür stehen uns 3 Funktionen zur Verfügung:
Err gibt uns die Fehlernummer an.
Errl gibt uns die Zeilennummer des Fehlers an (Achtung, funktioniert nur richtig, wenn der Fehler nicht in einer aufgerufenen selbst definierten Funktion oder Subroutine auftritt)
Error$ gibt uns den Text der Standard-Fehlermeldung an.
Beispiel:
Sub Demo
On Error Goto ErrHandler
... hier der Code
AfterError:
Print "Routine Demo beendet"
Exit Sub
ErrHandler:
Print "Der Fehler mit Nummer " & Str(Err) & " mit der Meldung " & Error$ & " ist bei Zeile " & Str(Errl) & " aufgetreten. Wir steigen aus"
Resume AfterError:
End Sub
Da gibt es jetzt zu allem noch die Möglichkeit, dass wir als Trittbrettfahrer auf das System aufspringen können: Mit dem Error-Statement können wir selber eigene Fehlernummern und Fehlermeldungen generieren:
Error 100
löst die Fehlerbehandlung mit Nummer 100 aus
Error 101,"Dies ist unser eigener Fehler"
macht dasselbe, erzeugt aber zusätzlich noch den Fehlertext "Dies ist unser eigener Fehler"
Mit Hilfe des Error-Statements lassen sich an beliebiger Stelle im Programm entweder eigene Fehler "produzieren" oder alle LotusScript Fehler simulieren (praktisch, wenn man nicht das richtige Umfeld zur Verfügung hat).
Beispiel:
Public x As Single
Const TOO_SMALL = 1001, TOO_BIG = 1002
Sub GetNum
Dim Num As String
On Error GoTo Errhandle
Num$= InputBox$("Enter a value between 1 and 100:")
x! = CSng(Num$) ' Convert the string to a 4-byte single.
' Check the validity of the entry.
If x! < 1 Then
Error TOO_SMALL, "The number is too small or negative."
ElseIf x! > 100 Then
Error TOO_BIG, "The number is too big."
End If
' If the script gets here, the user made a valid entry.
MessageBox "Good job! " & Num$ & " is a valid entry."
Exit Sub
' The user did not make a valid entry.
' Display the error number and error message.
Errhandle:
' Use the Err function to return the error number and
' the Error$ function to return the error message.
MessageBox "Error" & Str(Err) & ": " & Error$
Exit Sub
End Sub
Mich würde es mal konkret interessieren, wie man das bei einer Funktion wie folgender handhaben würde:
Function PurgingArray(strSourceArray As Variant ) As Variant
%REM
##########################################################################
# ID: 0001
# Creator: TMC (atnotes@gmx.de)
# Categories: Array-Handling
# Version: 0.1
# Date: 23. Nov. 2003
# Goal: Array alphabetisch sortieren und Leerzeilen entfernen
# Description:
#
# Remarks:
#
# Used Functions: keine
# Call: z.B. über "PurgingList(doc.IchBinDasFeld)"
# Tested: Action-Button in Maske
##########################################################################
%END REM
Dim k As Integer
Dim i As Integer
Dim j As Integer
Dim h As Integer
Dim r As Integer
Dim memberArray() As String
Dim temp As String
'Erstelle Array aus Werten zum sortieren
For k = 0 To Ubound(strSourceArray)
Redim Preserve memberArray(1 To k+1)
memberArray(k+1) = Cstr(strSourceArray(k))
Next
'Set up für den Sortier-Algorithmus
h = 1
Do While h < k
h = (h*3)+1
Loop
h = (h-1)/3
If h > 3 Then
h = (h-1)/3
End If
'Sorier-Algorithmus
Do While h > 0
For i = 1+h To k
temp = memberArray(i)
j = i-h
Do While j >0
If memberArray(j)>temp Then
memberArray(j+h) = memberArray(j)
memberArray(j) = temp
Else
Exit Do
End If
j = j-h
Loop
Next i
h = (h-1)/3
Loop
'Ergebnis
PurgingArray = memberArray
End Function
Die Funktion soll z.B. in einem Agenten laufen, der mehrere Dok-Felder in einer Collection abarbeitet, oder aber in einem Action-Button in einem Dokument, welches gerade im Bearbeitungsmodus geöffnet wird.
Was kann z.B. schiefgehen:
übergebender Feldinhalt ist kein String, sondern Nummer etc.
Könnte man ja über "If Not Datatype(strSourceArray) = 8 Then" abfragen.
Wie würde man denn als Rückgabewert der Funktion ein True / False einbauen und Fehlertext im False-Fall?
TMC
Ich grabe mal diesen etwas älteren Thread wieder aus.
Und da gleich eine Frage:
Angenommen ich habe einen Script-Abschnitt, wo ich erwarte, dass ein Error 91 auftreten kann.
Hier möchte ich diesen dann anders händeln als Global.
Also: Im Script selbst will ich z.B., wenn Error 91 auftritt, zu "errHandler" springen. Aber in einem bestimmten Codeabschnitt will ich - wenn der Error 91 auftritt, wo anders hinspringen.
Beispiel:
On Error goto errHandler
Code Code Code Code Code Code
Code Code Code Code Code Code
'---> Start: Hier möchte ich Error 91 speziell abfangen
Code Code Code Code Code Code
Code Code Code Code Code Code
'<---- Ende
Code Code Code Code Code Code
Code Code Code Code Code Code
Code Code Code Code Code Code
exitScript:
Exit Sub
errHandler:
Select Case Err
Case 91
Resume Sprung1
Case Else
Msgbox "Es ist ein Fehler aufgetreten." & Chr(10) & Chr(10) _
& "Fehlermeldung: " & Error$ & Chr(10) _
& "Fehlernummer: " & Err & Chr(10) _
& "Codezeile: " & Erl & Chr(10) _
,64,"Error"
Resume exitScript
End Select
Wie würde man sowas umsetzen?
Matthias
Thomas, würde das so ungefähr dann klappen?
On Error goto errHandler
Code Code Code Code Code Code
Code Code Code Code Code Code
On Error goto Hell
Code Code Code Code Code Code
Code Code Code Code Code Code
On Error goto errHandler
Code Code Code Code Code Code
Code Code Code Code Code Code
Code Code Code Code Code Code
exitScript:
Exit Sub
Hell:
praytogod
Exit Sub
errHandler:
Select Case Err
Case 91
Resume Sprung1
Case Else
Msgbox "Es ist ein Fehler aufgetreten." & Chr(10) & Chr(10) _
& "Fehlermeldung: " & Error$ & Chr(10) _
& "Fehlernummer: " & Err & Chr(10) _
& "Codezeile: " & Erl & Chr(10) _
,64,"Error"
Resume exitScript
End Select
Also Error nimmt so temoprär die neue Sprungmarke Hell an, und man stellt das einfach dann wieder zurück?
8)
Matthias
Ich habe mir jetzt mal eine eigene Display-Error - Sub erstellt, die ich Euch nicht vorenthalten will.
Insbesondere hat mich Getthreadinfo interessiert.
Wie Andreas schon angemerkt hat, weicht LSI_THREAD_LINE ab von der tatsächlichen Zeilennummer des Fehlers. Ich habe festgestellt, dass Leerzeilen nicht mitgezählt werden.
Da wir aber in Script eh "Erl" zur Verfügung haben, brauche ich das auch nicht.
Sub DisplayError
Dim strTitle As String
Dim strMsg As String
Dim vProcedure As Variant
vProcedure = Getthreadinfo( LSI_THREAD_CALLPROC )
strTitle = "An error occured"
strMsg = _
"Error: " & Err & " - " & Error$ & Chr(10) & Chr(10)_
& "Procedure: " & vProcedure & Chr(10) & Chr(10) _
& "Line: " & Erl & Chr(10)
Msgbox strMsg , 48, strTitle
End Sub
Gedacht ist die Sub für die ScriptLibrary.
Wichtig: in die Options der ScriptLib muss die Zeile
, damit Getthreadinfo funktioniert.
So funktioniert das ganze fein. Ich habe mal in mehreren Functions im Errorhandler die Zeile "Call DisplayError" eingefügt, und es wird sauber der Function-Name angezeigt.
Matthias