Fehlerbehandlung in VBA
On Error Goto
ErrorHandlerLabel
Resume
( Next
| ErrorHandlerLabel )
On Error Goto 0
(Deaktiviert die aktuelle Fehlerbehandlungsroutine)
Err
Objekt
Die Err
Eigenschaften des Objekts werden normalerweise in der Fehlerbehandlungsroutine auf Null oder eine Zeichenfolge mit der Länge Null zurückgesetzt, dies kann jedoch auch explizit mit erfolgen Err.Clear
.
Fehler in der Fehlerbehandlungsroutine werden beendet.
Der Bereich 513-65535 ist für Benutzerfehler verfügbar. Bei benutzerdefinierten Klassenfehlern fügen Sie vbObjectError
die Fehlernummer hinzu. Siehe MS-Dokumentation zu Err.Raise
und die Liste der Fehlernummern .
Für nicht implementierte Schnittstellenelemente in einer abgeleiteten Klasse sollten Sie die Konstante verwenden E_NOTIMPL = &H80004001
.
Option Explicit
Sub HandleError()
Dim a As Integer
On Error GoTo errMyErrorHandler
a = 7 / 0
On Error GoTo 0
Debug.Print "This line won't be executed."
DoCleanUp:
a = 0
Exit Sub
errMyErrorHandler:
MsgBox Err.Description, _
vbExclamation + vbOKCancel, _
"Error: " & CStr(Err.Number)
Resume DoCleanUp
End Sub
Sub RaiseAndHandleError()
On Error GoTo errMyErrorHandler
Err.Raise vbObjectError + 513, "Module1::Test()", "My custom error."
On Error GoTo 0
Debug.Print "This line will be executed."
Exit Sub
errMyErrorHandler:
MsgBox Err.Description, _
vbExclamation + vbOKCancel, _
"Error: " & CStr(Err.Number)
Err.Clear
Resume Next
End Sub
Sub FailInErrorHandler()
Dim a As Integer
On Error GoTo errMyErrorHandler
a = 7 / 0
On Error GoTo 0
Debug.Print "This line won't be executed."
DoCleanUp:
a = 0
Exit Sub
errMyErrorHandler:
a = 7 / 0
Ich würde auch hinzufügen:
Err
Objekt kommt einem Ausnahmeobjekt am nächstenErr.Raise
Und nur zum Spaß:
On Error Resume Next
ist der inkarnierte Teufel und zu vermeiden, da er Fehler stillschweigend verbirgtquelle
Call mdl_val.usr_sub_handle_error(Err.Source, Err.Description)
On Error Resume Next
. Das gemeinsame Prinzip dieser Fälle ist, wenn einige Ergebnisse durch Auslösen einer Ausnahme zurückgegeben werden. Der häufigste Fall ist der Zugriff auf einCollection
Objekt über den Zeichenfolgenschlüssel: In diesem Fall kann der Aufrufer nicht wissen, ob sich imCollection
Objekt ein Element mit diesem Schlüssel befindet .Sie könnten also so etwas tun
Function Errorthingy(pParam) On Error GoTo HandleErr ' your code here ExitHere: ' your finally code Exit Function HandleErr: Select Case Err.Number ' different error handling here' Case Else MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "ErrorThingy" End Select Resume ExitHere End Function
Wenn Sie benutzerdefinierte Ausnahmen backen möchten. (z. B. solche, die gegen Geschäftsregeln verstoßen) Verwenden Sie das obige Beispiel, aber verwenden Sie das Goto, um den Ablauf der Methode nach Bedarf zu ändern.
quelle
Resume ExitHere
, was einen großen Unterschied zwischen den beiden Paradigmen macht.Hier ist meine Standardimplementierung. Ich mag es, wenn die Etiketten selbstbeschreibend sind.
Public Sub DoSomething() On Error GoTo Catch ' Try ' normal code here Exit Sub Catch: 'error code: you can get the specific error by checking Err.Number End Sub
Oder mit einem
Finally
Block:Public Sub DoSomething() On Error GoTo Catch ' Try ' normal code here GoTo Finally Catch: 'error code Finally: 'cleanup code End Sub
quelle
Finally:
? AlsoOn Error GoTo 0
sofort danach mussFinally:
vielleicht die unerwünschte Rekursion behoben werden.Finally
Block ein Fehler auftritt, wird nur der Fehler ausgegeben. Es wird nicht wieder zumFinally
Block zurückgeschleift. (Probieren Sie es aus, Sie werden sehen.) Wenn Sie einen Fehler nach dem Block "finally" behandeln möchten, müssen Sie einen weiteren hinzufügenOn Error GoTo
, wahrscheinlich jedoch mit einem anderen Label, zCatch2
. Aber hier fangen wir an, uns mit der Clean Code-Methodik zu befassen -> eine Clean-Methode benötigt nur einen Fehlerbehandler (und sollte sogar eine eigene Methode zur Fehlererkennung haben).Finally:
hängt davon ab, ob Sie es betreten, nachdem Sie umgeleitet wurdenCatch:
(dann wirft es ja einfach raus) ... oder nicht ! Und in diesem letzteren Fall, dh wenn Sie es durchlaufenGoTo Finally
haben, ist dasOn Error GoTo Catch
noch in Kraft, sodass die Steuerung umgeleitet wirdCatch:
(könnte eine gute Sache sein) und dannFinally:
erneut eingegeben wird, wahrscheinlich nicht das, was Sie ursprünglich erwartet haben.On Error GoTo Catch2
inFinally:
Code, wird es in diesem letzteren Fall wirksam gewesen , aber nicht , wenn Sie ging durchCatch:
vor, weil es keineOn Error GoTo -1
noch einResume
; Das Hinzufügen des ersteren bringt uns so weit vom Normalen weg,try catch finally
dass man erwägen könnte, zweifelhafte Analogien vor diesem Punkt zu beenden.Professionelle Excel-Entwicklung hat ein ziemlich gutes Fehlerbehandlungsschema . Wenn Sie Zeit in VBA verbringen möchten, lohnt es sich wahrscheinlich, das Buch zu bekommen. Es gibt eine Reihe von Bereichen, in denen VBA fehlt, und dieses Buch enthält gute Vorschläge für die Verwaltung dieser Bereiche.
PED beschreibt zwei Fehlerbehandlungsmethoden. Das wichtigste ist ein System, bei dem alle Einstiegspunktprozeduren Unterprozeduren und alle anderen Prozeduren Funktionen sind, die Boolesche Werte zurückgeben.
Die Einstiegspunktprozedur verwendet On Error-Anweisungen, um Fehler so gut wie geplant zu erfassen. Die Prozeduren ohne Einstiegspunkt geben True zurück, wenn keine Fehler aufgetreten sind, und False, wenn Fehler aufgetreten sind. Prozeduren, die keine Einstiegspunkte sind, verwenden ebenfalls On Error.
Beide Arten von Prozeduren verwenden eine zentrale Fehlerbehandlungsprozedur, um den Fehler im Status zu halten und den Fehler zu protokollieren.
quelle
Ich verwende einen Code, den ich selbst entwickelt habe und der für meine Codes ziemlich gut ist:
Am Anfang der Funktion oder des Sub definiere ich:
On error Goto ErrorCatcher:
und dann behandle ich die möglichen Fehler
ErrorCatcher: Select Case Err.Number Case 0 'exit the code when no error was raised On Error GoTo 0 Exit Function Case 1 'Error on definition of object 'do stuff Case... 'little description here 'do stuff Case Else Debug.Print "###ERROR" Debug.Print " • Number :", Err.Number Debug.Print " • Descrip :", Err.Description Debug.Print " • Source :", Err.Source Debug.Print " • HelpCont:", Err.HelpContext Debug.Print " • LastDLL :", Err.LastDllError Stop Err.Clear Resume End Select
quelle
Hier ist ein ziemlich anständiges Muster.
Zum Debuggen: Wenn ein Fehler auftritt, drücken Sie Strg-Pause (oder Strg-Pause), ziehen Sie die Unterbrechungsmarkierung (oder wie auch immer sie heißt) nach unten in die Zeile Fortsetzen, drücken Sie F8 und Sie gelangen zu der Zeile, die "geworfen" hat. der Fehler.
Der ExitHandler ist Ihr "Endlich".
Sanduhr wird jedes Mal getötet. Der Text in der Statusleiste wird jedes Mal gelöscht.
Public Sub ErrorHandlerExample() Dim dbs As DAO.Database Dim rst As DAO.Recordset On Error GoTo ErrHandler Dim varRetVal As Variant Set dbs = CurrentDb Set rst = dbs.OpenRecordset("SomeTable", dbOpenDynaset, dbSeeChanges + dbFailOnError) Call DoCmd.Hourglass(True) 'Do something with the RecordSet and close it. Call DoCmd.Hourglass(False) ExitHandler: Set rst = Nothing Set dbs = Nothing Exit Sub ErrHandler: Call DoCmd.Hourglass(False) Call DoCmd.SetWarnings(True) varRetVal = SysCmd(acSysCmdClearStatus) Dim errX As DAO.Error If Errors.Count > 1 Then For Each errX In DAO.Errors MsgBox "ODBC Error " & errX.Number & vbCrLf & errX.Description Next errX Else MsgBox "VBA Error " & Err.Number & ": " & vbCrLf & Err.Description & vbCrLf & "In: Form_MainForm", vbCritical End If Resume ExitHandler Resume End Sub Select Case Err.Number Case 3326 'This Recordset is not updateable 'Do something about it. Or not... Case Else MsgBox "VBA Error " & Err.Number & ": " & vbCrLf & Err.Description & vbCrLf & "In: Form_MainForm", vbCritical End Select
Es werden auch DAO- und VBA-Fehler abgefangen. Sie können einen Auswahlfall in den Abschnitt VBA-Fehler einfügen, wenn Sie nach bestimmten Fehlernummern suchen möchten.
Select Case Err.Number Case 3326 'This Recordset is not updateable 'Do something about it. Or not... Case Else MsgBox "VBA Error " & Err.Number & ": " & vbCrLf & Err.Description & vbCrLf & "In: Form_MainForm", vbCritical End Select
quelle
Der folgende Code zeigt eine Alternative, die sicherstellt, dass es nur einen Austrittspunkt für das Unter / die Funktion gibt.
sub something() on error goto errHandler ' start of code .... .... 'end of code ' 1. not needed but signals to any other developer that looks at this ' code that you are skipping over the error handler... ' see point 1... err.clear errHandler: if err.number <> 0 then ' error handling code end if end sub
quelle
Ebenfalls relevant für die Diskussion ist die relativ unbekannte
Erl
Funktion. Wenn Ihre Code-Prozedur numerische Beschriftungen enthält, z.Sub AAA() On Error Goto ErrorHandler 1000: ' code 1100: ' more code 1200: ' even more code that causes an error 1300: ' yet more code 9999: ' end of main part of procedure ErrorHandler: If Err.Number <> 0 Then Debug.Print "Error: " + CStr(Err.Number), Err.Descrption, _ "Last Successful Line: " + CStr(Erl) End If End Sub
Die
Erl
Funktion gibt die zuletzt gefundene Beschriftung der numerischen Zeilen zurück. Wenn im obigen Beispiel ein Laufzeitfehler nach dem Beschriften,1200:
aber vorher auftritt1300:
, wird dieErl
Funktion zurückgegeben1200
, da dies die am häufigsten auftretende Zeilenbeschriftung ist. Ich halte es für eine gute Praxis, eine Zeilenbeschriftung direkt über Ihrem Fehlerbehandlungsblock anzubringen. Ich benutze normalerweise,9999
um anzuzeigen, dass der Hauptteil der Prozedur zu seinem erwarteten Konkuls lief.ANMERKUNGEN:
MadeItHere:
Zeilenbeschriftungen MÜSSEN positive ganze Zahlen sein - eine Beschriftung wie wird von nicht erkanntErl
.Zeilenbeschriftungen haben nichts mit den tatsächlichen Zeilennummern von a zu tun
VBIDE CodeModule
. Sie können beliebige positive Zahlen in beliebiger Reihenfolge verwenden. Im obigen Beispiel gibt es nur etwa 25 Codezeilen, aber die Zeilennummern beginnen bei1000
. Es gibt keine Beziehung zwischen den Zeilennummern des Editors und den mit verwendeten ZeilennummernErl
.Die Zeilenbeschriftungsnummern müssen nicht in einer bestimmten Reihenfolge angegeben werden. Wenn sie jedoch nicht in aufsteigender Reihenfolge von oben nach unten angeordnet sind, ist die Wirksamkeit und der Nutzen von
Erl
stark beeinträchtigt, esErl
wird jedoch weiterhin die richtige Nummer angegeben.Linienbeschriftungen sind spezifisch für das Verfahren, in dem sie angezeigt werden. Wenn die Prozedur
ProcA
die Prozedur aufruftProcB
und ein Fehler auftrittProcB
, der die Steuerung an zurückgibt,ProcA
gibtErl
(inProcA
) die zuletzt angetroffene Zeilennummer zurück,ProcA
bevor sie aufgerufen wirdProcB
. Von innenProcA
können Sie die Zeilenbeschriftungsnummern, die möglicherweise in angezeigt werden, nicht abrufenProcB
.Seien Sie vorsichtig, wenn Sie Zeilennummern in eine Schleife einfügen. Zum Beispiel,
For X = 1 To 100 500: ' some code that causes an error 600: Next X
Wenn der Code nach der Zeilenbezeichnung,
500
jedoch vorher,600
einen Fehler verursacht und dieser Fehler bei der 20. Iteration der Schleife auftritt ,Erl
wird er zurückgegeben500
, obwohl600
er in den vorherigen 19 Interaktionen der Schleife erfolgreich aufgetreten ist.Die richtige Platzierung von Linienbeschriftungen innerhalb des Verfahrens ist entscheidend für die Verwendung der
Erl
Funktion, um wirklich aussagekräftige Informationen zu erhalten.Es gibt eine beliebige Anzahl kostenloser Dienstprogramme im Internet, die automatisch numerische Zeilenbeschriftungen in eine Prozedur einfügen, sodass Sie beim Entwickeln und Debuggen feinkörnige Fehlerinformationen erhalten und diese Beschriftungen entfernen, sobald der Code live geschaltet wird.
Wenn Ihr Code dem Endbenutzer Fehlerinformationen anzeigt, wenn ein unerwarteter Fehler auftritt, kann die Angabe des Werts aus
Erl
diesen Informationen das Auffinden und Beheben des Problems VAST einfacher machen, als wenn der Wert vonErl
nicht gemeldet wird.quelle
Ich finde, dass Folgendes am besten funktioniert, der als zentraler Ansatz zur Fehlerbehandlung bezeichnet wird.
Leistungen
Sie haben zwei Möglichkeiten, Ihre Anwendung auszuführen : Debug und Produktion . Im Debug- Modus stoppt der Code bei jedem unerwarteten Fehler und ermöglicht Ihnen ein einfaches Debuggen, indem Sie durch zweimaliges Drücken von F8 zu der Zeile springen, in der er aufgetreten ist. Im Produktionsmodus wird dem Benutzer eine aussagekräftige Fehlermeldung angezeigt.
Sie können absichtliche Fehler wie diesen auslösen, wodurch die Ausführung des Codes mit einer Nachricht an den Benutzer gestoppt wird:
Err.Raise vbObjectError, gsNO_DEBUG, "Some meaningful error message to the user" Err.Raise vbObjectError, gsUSER_MESSAGE, "Some meaningful non-error message to the user" 'Or to exit in the middle of a call stack without a message: Err.Raise vbObjectError, gsSILENT
Implementierung
Sie müssen alle Unterprogramme und Funktionen mit einer signifikanten Menge an Code mit den folgenden Kopf- und Fußzeilen "umschließen" und dabei sicherstellen, dass Sie
ehCallTypeEntryPoint
alle Ihre Einstiegspunkte angeben . Beachten Sie auch diemsModule
Konstante, die in alle Module eingefügt werden muss.Option Explicit Const msModule As String = "<Your Module Name>" ' This is an entry point Public Sub AnEntryPoint() Const sSOURCE As String = "AnEntryPoint" On Error GoTo ErrorHandler 'Your code ErrorExit: Exit Sub ErrorHandler: If CentralErrorHandler(Err, ThisWorkbook, msModule, sSOURCE, ehCallTypeEntryPoint) Then Stop Resume Else Resume ErrorExit End If End Sub ' This is any other subroutine or function that isn't an entry point Sub AnyOtherSub() Const sSOURCE As String = "AnyOtherSub" On Error GoTo ErrorHandler 'Your code ErrorExit: Exit Sub ErrorHandler: If CentralErrorHandler(Err, ThisWorkbook, msModule, sSOURCE) Then Stop Resume Else Resume ErrorExit End If End Sub
Der Inhalt des zentralen Fehlerbehandlungsmoduls ist folgender:
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Comments: Error handler code. ' ' Run SetDebugMode True to use debug mode (Dev mode) ' It will be False by default (Production mode) ' ' Author: Igor Popov ' Date: 13 Feb 2014 ' Licence: MIT ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Option Explicit Option Private Module Private Const msModule As String = "MErrorHandler" Public Const gsAPP_NAME As String = "<You Application Name>" Public Const gsSILENT As String = "UserCancel" 'A silent error is when the user aborts an action, no message should be displayed Public Const gsNO_DEBUG As String = "NoDebug" 'This type of error will display a specific message to the user in situation of an expected (provided-for) error. Public Const gsUSER_MESSAGE As String = "UserMessage" 'Use this type of error to display an information message to the user Private Const msDEBUG_MODE_COMPANY = "<Your Company>" Private Const msDEBUG_MODE_SECTION = "<Your Team>" Private Const msDEBUG_MODE_VALUE = "DEBUG_MODE" Public Enum ECallType ehCallTypeRegular = 0 ehCallTypeEntryPoint End Enum Public Function DebugMode() As Boolean DebugMode = CBool(GetSetting(msDEBUG_MODE_COMPANY, msDEBUG_MODE_SECTION, msDEBUG_MODE_VALUE, 0)) End Function Public Sub SetDebugMode(Optional bMode As Boolean = True) SaveSetting msDEBUG_MODE_COMPANY, msDEBUG_MODE_SECTION, msDEBUG_MODE_VALUE, IIf(bMode, 1, 0) End Sub ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Comments: The central error handler for all functions ' Displays errors to the user at the entry point level, or, if we're below the entry point, rethrows it upwards until the entry point is reached ' ' Returns True to stop and debug unexpected errors in debug mode. ' ' The function can be enhanced to log errors. ' ' Date Developer TDID Comment ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' 13 Feb 2014 Igor Popov Created Public Function CentralErrorHandler(ErrObj As ErrObject, Wbk As Workbook, ByVal sModule As String, ByVal sSOURCE As String, _ Optional enCallType As ECallType = ehCallTypeRegular, Optional ByVal bRethrowError As Boolean = True) As Boolean Static ssModule As String, ssSource As String If Len(ssModule) = 0 And Len(ssSource) = 0 Then 'Remember the module and the source of the first call to CentralErrorHandler ssModule = sModule ssSource = sSOURCE End If CentralErrorHandler = DebugMode And ErrObj.Source <> gsNO_DEBUG And ErrObj.Source <> gsUSER_MESSAGE And ErrObj.Source <> gsSILENT If CentralErrorHandler Then 'If it's an unexpected error and we're going to stop in the debug mode, just write the error message to the immediate window for debugging Debug.Print "#Err: " & Err.Description ElseIf enCallType = ehCallTypeEntryPoint Then 'If we have reached the entry point and it's not a silent error, display the message to the user in an error box If ErrObj.Source <> gsSILENT Then Dim sMsg As String: sMsg = ErrObj.Description If ErrObj.Source <> gsNO_DEBUG And ErrObj.Source <> gsUSER_MESSAGE Then sMsg = "Unexpected VBA error in workbook '" & Wbk.Name & "', module '" & ssModule & "', call '" & ssSource & "':" & vbCrLf & vbCrLf & sMsg MsgBox sMsg, vbOKOnly + IIf(ErrObj.Source = gsUSER_MESSAGE, vbInformation, vbCritical), gsAPP_NAME End If ElseIf bRethrowError Then 'Rethrow the error to the next level up if bRethrowError is True (by Default). 'Otherwise, do nothing as the calling function must be having special logic for handling errors. Err.Raise ErrObj.Number, ErrObj.Source, ErrObj.Description End If End Function
Führen Sie im Sofortfenster Folgendes aus, um sich in den Debug- Modus zu versetzen :
SetDebugMode True
quelle
Vorsicht vor der Elefantenfalle:
Ich habe dies in dieser Diskussion nicht erwähnt. [Zugriff 2010]
Wie ACCESS / VBA mit Fehlern in CLASS-Objekten umgeht, wird durch eine konfigurierbare Option bestimmt:
VBA-Code-Editor> Extras> Optionen> Allgemein> Fehlerbehebung:
quelle
Meine persönliche Meinung zu einer Aussage, die früher in diesem Thread gemacht wurde:
Ich verwende die
On Error Resume Next
on-Prozeduren, bei denen ich nicht möchte, dass ein Fehler meine Arbeit beendet, und bei denen eine Anweisung nicht vom Ergebnis der vorherigen Anweisungen abhängt.Wenn ich das mache, füge ich eine globale Variable hinzu
debugModeOn
und setze sie aufTrue
. Dann benutze ich es so:If not debugModeOn Then On Error Resume Next
Wenn ich meine Arbeit liefere, setze ich die Variable auf false, wodurch die Fehler nur dem Benutzer ausgeblendet und während des Tests angezeigt werden.
Verwenden Sie es auch, wenn Sie etwas tun, das möglicherweise fehlschlägt, z. B. den DataBodyRange eines ListObject aufrufen, das möglicherweise leer ist:
On Error Resume Next Sheet1.ListObjects(1).DataBodyRange.Delete On Error Goto 0
Anstatt:
If Sheet1.ListObjects(1).ListRows.Count > 0 Then Sheet1.ListObjects(1).DataBodyRange.Delete End If
Oder die Existenz eines Artikels in einer Sammlung überprüfen:
On Error Resume Next Err.Clear Set auxiliarVar = collection(key) ' Check existence (if you try to retrieve a nonexistant key you get error number 5) exists = (Err.Number <> 5)
quelle
If not debugModeOn Then On Error Resume Next
In diesem Fall ist es besser, die bedingte Kompilierung wie zu verwenden#If Hide_Errors > 0 Then On Error Resume Next
undHide_Errors
in den VBA-ProjekteigenschaftenConditional Complication Arguments
entsprechend festzulegen .