Anzeige
Archiv - Navigation
1164to1168
Aktuelles Verzeichnis
Verzeichnis Index
Übersicht Verzeichnisse
Vorheriger Thread
Rückwärts Blättern
Nächster Thread
Vorwärts blättern
Anzeige
HERBERS
Excel-Forum (Archiv)
20+ Jahre Excel-Kompetenz: Von Anwendern, für Anwender
Inhaltsverzeichnis

saubere Fehlerbehandlung

saubere Fehlerbehandlung
JogyB
Hallo zusammen,
ein Beitrag von Nepumuk hat mich auf ein Problem gebracht, dass mir so nicht bewußt war, nämlich dass bei "On Error GoTo" die Fehlerbehandlung erst beendet werden muss (durch Resume oder Beenden des Sub bzw. der Funktion), sonst führt ein nachfolgender Fehler zu einer Fehlermeldung (klarer Fall von RTFM, ich weiß)
https://www.herber.de/forum/messages/1167324.html
(habe an der Stelle auch nochmal nachgefragt, da der Thread aber um ein anderes, mittlerweile erledigtes Thema ging, schaut wohl niemand mehr rein)
Nun ist es bei mir in der Regel so, dass ich meinen Code stark zerlege, so dass ein Sub oder eine Funktion entweder fehlerfrei durchläuft oder nach dem Fehler abgebrochen werden muss (und ggf. einen Fehler an die aufrufende Prozedur zurückgibt), d.h. "Resume" wird bei mir so gut wie nie verwendet.
Das Problem ist, dass ich in der Fehlerbehandlung dann gerne solche Dinge erledige, wie bspw. die während der Ausführung der Prozedur geöffneten Arbeitsmappen wieder zu schliessen oder temporär erstellete Arbeitsblätter zu löschen. Bislang habe ich das so gemacht:

errorHandler:
On Error Resume Next
Workbooks("Quelldaten.xls").Close False
Exit Sub

Der Hintergedanke war, dass ich mir so keine Sorgen machen muss, ob die Arbeitsmappe nun vor dem Fehler noch geöffnet wurde oder nicht - ist sie da, wird sie geschlossen, wenn nicht, ist es auch egal. Das funktioniert natürlich nicht, da die Fehlerbehandlung des ursprünglichen Fehlers noch läuft.
Mir ist auch durchaus klar, dass ich mit mehreren Sprungmarken oder Abfragen hier theoretisch alles abfangen kann, nur wenn mal mehrere Arbeitsmappen geöffnet und wieder geschlossen werden, dann ist das doch ein ziemlicher Aufwand dafür, dass letztendlich nur alle wieder zu sein sollen. Nun könnte ich es natürlich auch so machen:

errorHandler:
Resume endError
endError:
On Error Resume Next
Workbooks("Quelldaten.xls").Close False
End Sub

Das sieht aber nicht sonderlich elegant aus. Gibt es da eine bessere Lösung?
Danke und Gruß,
Jogy
AW: Fehlerbehandlung - Links
19.07.2010 11:58:16
JogyB
Hallo Beate,
danke für die Links, die kommen auf jeden Fall in meine Linksammlung, auch wenn mir die Punkte im wesentlichen klar waren. Ist aber eine schöne Übersicht zu dem Thema.
Mir geht es konkret um den Fall, dass in einer Fehlerbehandlung weitere Fehler auftreten können. Und zwar Fehler die mit "Mir ist es Wurscht, dass ein Fehler passiert ist." behandelt werden können - wie eben das Beispiel mit der Arbeitsmappe. Wenn sie offen war, wird sie geschlossen, wenn nicht, dann soll der Fehler ignoriert werden... hauptsache am Ende ist sie zu.
Gruß, Jogy
AW: Fehlerbehandlung - Links
19.07.2010 12:22:31
Ramses
Hallo
folgendes Szenarion löst das problem
Option Explicit

Sub Error_Handler()
    Dim i As Integer
    On Error GoTo myError
    'sonstiger Code voraus
    '---
    On Error Resume Next
    i = 1 / 0
    On Error GoTo myError
    MsgBox "noch kein Fehler"
    i = 1 / 0
    errorExit:
    Exit Sub
    
    myError:
    MsgBox "Nun ist der Fehler da"
    Resume errorExit
End Sub

Einfach die On Error Anweisung neu setzen.
Damit kannst du explicit Fehler, die in einem bestimmten Bereich auftreten, ausschliessen und anschliessend die Fehlerbehandlung wieder starten
Gruss Rainer
Anzeige
AW: Fehlerbehandlung - Links
19.07.2010 13:20:55
JogyB
Hallo Rainer,
das trifft leider auch nicht mein Problem. Mir geht es eher um so etwas:
Sub Error_Handler()
Dim i As Integer
On Error GoTo myError
' irgendwelcher Code
i = 1 / 0
' irgendwelcher Code
Exit Sub
myError:
On Error Resume Next
' Code, der bei einem Fehler ausgeführt werden soll
i = 2 / 0
' weiterer Code, der bei einem Fehler ausgeführt werden soll
End Sub

Das i = 1 / 0 kann jetzt irgendwo in dem Code sein, ich muss also "On Error GoTo" verwenden.
Und das i = 2 / 0 ist irgendwo nach der Sprungmarke und erzeugt nicht jedesmal einen Fehler, das ist abhängig von der Stelle des Auftauchens des ersten Fehlers.
So funktioniert es aber nicht, da solange die Fehlerbehandlung des ersten Fehlers noch läuft, der zweite Fehler in jedem Fall zu einer Fehlermeldung führt.
So funktioniert es, sieht aber übel aus:
Sub Error_Handler()
Dim i As Integer
On Error GoTo myError
' irgendwelcher Code
i = 1 / 0
' irgendwelcher Code
Exit Sub
myError:
Resume endError
endError:
On Error Resume Next
' Code, der bei einem Fehler ausgeführt werden soll
i = 2 / 0
' weiterer Code, der bei einem Fehler ausgeführt werden soll
End Sub

Im Endeffekt wäre der Befehl, den ich gerne hätte, ein On Error Resume [Sprungmarke], aber das gibt es ja leider nicht.
Gruß, Jogy
Anzeige
AW: Fehlerbehandlung - Links
19.07.2010 13:35:55
Ramses
Hallo
"...Das i = 1 / 0 kann jetzt irgendwo in dem Code sein..."
Nein, tut es nicht. Es steht an einer definierten Stelle.
Wenn du wie du sagst, soviel in Funktionen auslagerst, dann weisst du doch wo ein Fehler auftreten kann.
Wo ist das Problem ?
Alternativ bau dir deinen eigenen Fehlerhandler
Option Explicit

Sub Error_Handler()
    Dim i As Integer
    Dim myError As Long
    On Error GoTo myError
    'sonstiger Code voraus
    '---
    myError = 1
    i = 1 / 0
    MsgBox "noch kein Fehler"
    myError2:
    myError = 2
    i = 1 / 0
    weiter:
    MsgBox "hier gehts weiter"
    Worksheets("NichtDa").Select
    errorExit:
    Exit Sub
    
    myError:
    Select Case myError
        Case 1
            myError = 0
            Resume myError2
        Case 2
            MsgBox "Fehler"
            myError = 0
            Resume weiter
    End Select
    If Err <> 0 Then
        MsgBox "Neuer Fehler: " & Err.Number & ": " & Err.Description
    End If
    Resume errorExit
End Sub

Ist zwar etwas umständlich, aber dafür kontrollierbar
Gruss Rainer
Anzeige
AW: Fehlerbehandlung - Links
19.07.2010 14:06:51
JogyB
Hallo Rainer,
genau das mit dem "kompliziert" wollte ich ja vermeiden ;). Ich habe nochmal ein Beispiel gebastelt:
Function fehlerTest() As Boolean
Dim errCode As Integer
Const pfAd = "c:\temp\"
Const datei1 = "datei1.xls"
Const datei2 = "datei2.xls"
On Error GoTo errorHandler
errCode = 0
Workbooks.Open (pfAd & datei1)
errCode = 1
With Workbooks(datei1)
.Cells(3, 1) = .Cells(2, 1) / .Cells(1, 1)
End With
Workbooks.Open (pfAd & datei2)
errCode = 2
With Workbooks(datei2)
.Cells(2, 1) = .Cells(1, 1) / Workbooks(datei1).Cells(4, 1)
Workbooks(datei1).Cells(4, 1) = .Cells(3, 1) / .Cells(4, 1)
.Close True
errCode = 1
End With
Workbooks(datei1).Close True
errCode = 0
fehlerTest = True
Exit Function
errorHandler:
' Das müßte noch ein, damit es geht
'    Resume endErr
'endErr:
On Error Resume Next
If errCode > 0 Then Workbooks(datei1).Close False
If errCode > 1 Then Workbooks(datei2).Close False
MsgBox ("Fehler bei der Verarbeitung der Dateien")
' zur Veranschaulichung, ist ja schon False beim Aufruf
fehlerTest = False
End Function

So hätte ich es gerne. Gut, in dem Beispiel wäre es jetzt interessant, wo der Fehler aufgetreten ist, in den meisten Anwendungsfällen reicht es mir jedoch, den Absturz des Programmes zu verhindern (der User soll an den Ausgangstabellen nicht von Hand herumpfuschen).
Klar, ich könnte es jetzt natürlich auch so machen:
FFunction fehlerTest2() As Boolean
Dim errCode As Integer
Const pfAd = "c:\temp\"
Const datei1 = "datei1.xls"
Const datei2 = "datei2.xls"
On Error GoTo errorHandler
errCode = 0
Workbooks.Open (pfAd & datei1)
errCode = 1
With Workbooks(datei1)
.Cells(3, 1) = .Cells(2, 1) / .Cells(1, 1)
End With
Workbooks.Open (pfAd & datei2)
errCode = 2
With Workbooks(datei2)
.Cells(2, 1) = .Cells(1, 1) / Workbooks(datei1).Cells(4, 1)
Workbooks(datei1).Cells(4, 1) = .Cells(3, 1) / .Cells(4, 1)
.Close True
errCode = 1
End With
Workbooks(datei1).Close True
errCode = 0
fehlerTest = True
Exit Function
errorHandler:
If errCode > 0 Then Workbooks(datei1).Close False
If errCode > 1 Then Workbooks(datei2).Close False
MsgBox ("Fehler bei der Verarbeitung der Dateien")
' zur Veranschaulichung, ist ja schon False beim Aufruf
fehlerTest = False
End Function
oder so:
Function fehlerTest3() As Boolean
Const pfAd = "c:\temp\"
Const datei1 = "datei1.xls"
Const datei2 = "datei2.xls"
On Error GoTo myError1
Workbooks.Open (pfAd & datei1)
On Error GoTo myError2
With Workbooks(datei1)
.Cells(3, 1) = .Cells(2, 1) / .Cells(1, 1)
End With
Workbooks.Open (pfAd & datei2)
On Error GoTo myError3
With Workbooks(datei2)
.Cells(2, 1) = .Cells(1, 1) / Workbooks(datei1).Cells(4, 1)
Workbooks(datei1).Cells(4, 1) = .Cells(3, 1) / .Cells(4, 1)
.Close True
On Error GoTo myError2
End With
Workbooks(datei1).Close True
On Error GoTo myError1
fehlerTest3 = True
Exit Function
myError3:
Workbooks(datei2).Close False
myError2:
Workbooks(datei1).Close False
myError1:
MsgBox ("Fehler bei der Verarbeitung der Dateien")
' zur Veranschaulichung, ist ja schon False beim Aufruf
fehlerTest3 = False
End Function

Nur hierbei muss ich sehr genau aufpassen, dass ich mich nirgends mit der Fehlerstelle verhasple, sonst ist es mit der Fehlerbehandlungsherrlichkeit vorbei. Das ist einfach der Charme des obigen Codes, falls - und nur in dem Fall - es mich nicht interessiert, wo der Fehler denn genau war.
Oder als anderes Beispiel, im Fehlerfall muss ein Teil des Codes übersprungen werden. Mein erster Gedanke war dann dieser:
Function fehlerTest4() As Integer
On Error GoTo myError1
' Code, bei dem in mehreren Zeilen Fehler entstehen können
' Taucht dort irgendwo ein Fehler auf, so soll der Codeteil
' abgebrochen werden
fehlerTest4 = 2 ' Bitwert, was erfolgreich war
myError1:
' Und hier soll es weitergehen, allerdings können hier auch
' Fehler auftreten
' Das müßte noch ein, damit es geht
'    Resume weiTer
'weiTer:
On Error GoTo myError2
' hier kommt nun der weitere Code
fehlerTest4 = fehlerTest4 + 1
Exit Function
myError2:
' da braucht nichts mehr zu passieren, kann einfach raus
End Function
Aber eigentlich müsste man es so lösen:
Function fehlerTest5() As Integer
On Error GoTo myError1
' Code, bei dem in mehreren Zeilen Fehler entstehen können
' Taucht dort irgendwo ein Fehler auf, so soll der Codeteil
' abgebrochen werden
fehlerTest5 = 2 ' Bitwert, was erfolgreich war
weiTer:
' Und hier soll es weitergehen, allerdings können hier auch
' Fehler auftreten
On Error GoTo myError2
' hier kommt nun der weitere Code
fehlerTest5 = fehlerTest5 + 1
Exit Function
myError1:
Resume weiTer
myError2:
' da braucht nichts mehr zu passieren, kann einfach raus
End Function
Wenn es nicht anders geht, dann muss ich es eben irgendwie so machen, letztendlich funktionieren alle Beispiele (1 und 4 natürlich nur, wenn die auskommentierten "Resume"-Befehle hereingenommen werden). Aber wie gesagt, sieht entweder nicht schön aus oder ist umständlich.
Gruß, Jogy
Anzeige
AW: Fehlerbehandlung - Links
19.07.2010 14:16:47
Ramses
Hallo
"..dass ich mich nirgends mit der Fehlerstelle verhasple, sonst ist es mit der Fehlerbehandlungsherrlichkeit vorbei. Das ist einfach der Charme des obigen Codes,.."
Wenn MS für alles einen spezifischen Fehlercode hätte, dann könnte man das so machen.
Hat es aber nciht. Der Fehler 1004 kommt bei zu vielen Sachen, aber vornehmlich wenn was nicht da ist
Wenn du soviel hin- und hersprinst, dann muss der Code keinen Charme haben, sondern funktionieren :-)
du kannst natürlich auch mit
Select Case Err.Number
arbeiten. Der Fehler 9 gibt an, dass ein Worksheet/Workbook nicht da ist, von da aus kannst du dann verzweigen.
Die Sprungmarken musst du ja auf jeden Fall wissen
Alternativ kannst du für jede externe Funktion noch eine Boolean-Variable anlegen
Public bolTest as boolean
Function test(var1 as long, var2 as long) as long
if isError(var1/var2) then
bolTest = false
else
test = var1/var2
bolTest = true
end if
End Function
In deinem Code kannst du dann nach Aufruf der Funktion die entsprechende Variable prüfen und weisst dann ob es funktioniert hat oder nicht.
test 5, 2
if bolTest = false then ....
Gruss Rainer
Anzeige
AW: Fehlerbehandlung - Links
19.07.2010 14:50:58
JogyB
Hallo Rainer,
ich glaube wir reden ein wenig aneinander vorbei, das muss ich wegen der Überschrift des Eingangspostings wohl auch auf meine Kappe nehmen.
Wie ich eine wirklich schöne Fehlerbehandlung mache, bei der ich den Fehler exakt nachvollziehen kann, ist mir absolut bewusst.
Im konkreten Fall läuft es aber darauf hinaus, dass es überhaupt keine Rolle spielt, warum etwas nicht geklappt hat, sondern es ist nur wichtig zu wissen, dass etwas nicht geklappt hat. In welcher Zelle welcher Tabelle der Fehler aufgetreten ist weiß ich durch die Prozedur und eine laufende Nummer, die zu den Daten gehört. Und dann muss ich sowieso von Hand rangehen. Deswegen soll die Fehlerbehandlung hier bewusst einfach gehalten werden, also schließe geöffnete Arbeitsmappen, lösche temporäre Dateien, setzte globale Variablen auf die Ausgangswerte zurück etc.
Dafür dann mehrere Sprungmarken zu verwenden oder eine Fehlervariable ständig anzupassen, ist mit Kanonen auf Spatzen geschossen. Vor allem baue ich damit potentielle Fehlerquellen in den Code ein, ohne einen Zusatznutzen zu haben - ich werte die Fehlerstelle ja sowieso nicht aus.
Wenn es dafür keine elegantere Lösung gibt, als
    On Error GoTo errorHandler
' Programmcode
Exit Function
errorHandler:
Resume endErr
endErr:
On Error Resume Next
' Code zur Fehlerbehandlung

muss ich wohl damit leben bzw. - was ich teilweise schon gemacht habe - die kritischen Geschichten wie das Schließen von (evtl. gar nicht geöffneten) Arbeitsmappen in eigene Prozeduren mit separater Fehlerbehandlung auszulagern.
Danke und Gruß, Jogy
Anzeige
AW: Fehlerbehandlung - Links
19.07.2010 19:28:48
Tino
Hallo,
meinst Du vielleicht so etwas in der Art?
Sub Error_Test()
Dim strFehlerText$
On Error GoTo ErrorSammler:
Range("A0") = 15
Range("A1") = 8 / 0
Range("A1").Value = Range("A2").Value
Sheets("Tabelle999").Name = "xyz"
If strFehlerText$  "" Then
MsgBox strFehlerText$
End If
Exit Sub
ErrorSammler:
strFehlerText$ = strFehlerText$ & "F-Nr.: " & Err.Number & ": " & Err.Description & vbCr
Resume Next
End Sub
Gruß Tino
AW: Fehlerbehandlung - Links
19.07.2010 19:42:52
JogyB
Hallo Tino,
nein, leider auch nicht. Die Prozedur soll nach dem ersten Fehler beendet werden, nur müssen vor dem Beenden noch ein paar Befehle ausgeführt werden. Bei diesen Befehlen können Fehler auftreten, diese sollen aber ignoriert werden. Wenn ein "On Error Resume Next" nach der Sprungmarke funktionieren würde, wäre es genau das, was ich haben will.
Die genauen Fehlermeldung brauche ich wie gesagt nicht, da gebe ich nur an, bei welchem Programmteil der Fehler aufgetreten ist (und das evtl. erst auf Basis des Rückgabewertes in der aufrufenden Prozedur) - danach weiß ich bereits, wo ich manuell noch etwas in der Tabelle ändern muss.
Gruß, Jogy
Anzeige
AW: Fehlerbehandlung - Links
19.07.2010 20:07:10
Tino
Hallo,
bei mir funktioniert es.
Sub Error_Test()
On Error GoTo ErrorHandler:
Range("A0") = 15 'Fehler
Range("A1") = 8 / 0 'Fehler
Range("A1").Value = Range("A2").Value 'kein Fehler
Sheets("Tabelle999").Name = "xyz" 'Fehler
Exit Sub
ErrorHandler:
On Error Resume Next
Resume Next
End Sub
Gruß Tino
AW: Fehlerbehandlung - Links
20.07.2010 09:09:46
JogyB
Hallo Tino,
dass das funktioniert weiß ich. Nur soll es bei mir nicht mehr in den Code zurückgehen, nachdem der Fehler aufgetreten ist, es kommt als kein Resume am Ende - wie gesagt, die Prozedur soll kontrolliert beendet werden, aber mit anderen Befehlen als wenn sie normal durchlaufen würde.
Um es nochmal als Schema darzustellen:
Sub feHler()
On Error GoTo errorHandler
'normaler Code
Exit Sub
errorHandler:
' Code bei Fehler
' KEIN Resume, Prozedur soll Abbrechen
End Sub

Und mir geht es schlichtweg darum, dass alle Fehler im Abschnitt "Code bei Fehler" ignoriert werden. Wenn sich hier beispielsweise eine temporäre Datei nicht löschen läßt, dann ist das egal, im Temp-Ordner bleibt genug Mist liegen, da kommt es auf eine 20kB-Datei mehr auch nicht an (kommt ja nicht oft vor).
Und da funktioniert "On Error Resume Next" eben nicht.
Wenn sich das nur mit solchem Code lösen lässt:
Sub feHler2()
On Error GoTo errorHandler
'normaler Code
Exit Sub
error1:
On Error Resume Next
' Code bei Fehler
Exit Sub
errorHandler:
Resume error1
End Sub

dann muss ich damit leben. Mir ging es nur darum, ob es eine elegantere Lösung gibt, die ich übersehen habe.
Gruß, Jogy
Anzeige
noch offen - kwT
20.07.2010 09:40:00
JogyB
verstehe wahrscheinlich nicht was Du willst...
21.07.2010 16:52:13
Tino
Hallo,
Sub feHler2()
On Error GoTo errorHandler
'normaler Code
Exit Sub
errorHandler:
On Error Resume Next
' Code bei Fehler
End Sub
Gruß Tino
AW: verstehe wahrscheinlich nicht was Du willst...
21.07.2010 20:09:47
JogyB
Hallo Tino,
genau so funktioniert es eben nicht, probier es mal aus. Solange kein Resume kommt (oder die Prozedur beendet wird), läuft die Behandlung des Fehlers, der zum Sprung in den errorHandler geführt hat und jeder weitere Fehler führt zu einer Fehlermeldung - egal ob da nun "On Error Resume Next" steht oder nicht.
Gruß, Jogy
AW: verstehe wahrscheinlich nicht was Du willst...
21.07.2010 20:27:50
Tino
Hallo,
ja stimmt, dann lagere den errorHandler in eine separaten Prozedur aus.
Gruß Tino
AW: verstehe wahrscheinlich nicht was Du willst...
21.07.2010 20:31:56
Tino
Hallo,
oder so geht es auch.
Sub feHler2()
Dim intWert As Integer
 On Error GoTo ErrorHandler1:
    intWert = 5 * 10 ^ 90
 
 Exit Sub
    
ErrorHandler1: Resume ErrorHandler2
ErrorHandler2:
    On Error Resume Next
    intWert = 5 * 10 ^ 90
 End Sub
Gruß Tino
AW: verstehe wahrscheinlich nicht was Du willst...
21.07.2010 20:54:23
JogyB
Hallo Tino,
das Beispiel hatte ich schon in meinem Eingangsposting (und danach glaube ich auch nochmal). ;)
Mir ging es ja hauptsächlich darum, ob es eleganter geht, sieht irgendwie unschön aus mit den zwei Sprungmarken direkt hintereinander.
Das mit dem Auslagern habe ich teilweise schon gemacht, bspw. das Schliessen von Arbeitsmappen läuft in einer eigenen, (hoffentlich) fehlertoleranten Prozedur. Alles komplett auszulagern würde natürlich gehen, wäre aber wieder der Aufwand, den ich in diesem Fall gerne vermeiden wollte.
Naja, wenn es nicht anders geht, dann muss ich es eben mit den zwei Sprungmarken machen, auch wenn es meinen Sinn für Ästhetik verletzt ;). Trotzdem Danke an Beate, Rainer und Tino für die Hilfe.
Gruß, Jogy
AW: verstehe wahrscheinlich nicht was Du willst...
22.07.2010 07:10:28
Tino
Hallo,
auch wenn es meinen Sinn für Ästhetik verletzt
Funktionalität hat Vorrang ;-)
Gruß Tino

Beliebteste Forumthreads (12 Monate)

Anzeige

Beliebteste Forumthreads (12 Monate)

Anzeige
Anzeige
Anzeige