2013-09-27 5 views
6

Ich habe das Buch "Professional Excel Development" von Rob Bovey und es öffnet mir die Augen.VBA Excel Fehlerbehandlung - speziell in Funktionen - Professional Excel Development Style

Ich überarbeite meinen Code mit Fehlerbehandlung. Es gibt jedoch vieles, was ich nicht verstehe. Ich muss besonders wissen, wie man es richtig in Funktionen benutzt. Ich benutze die Bovey-Version der Fehlerbehandlung (unten). Als ich anfing, benutzte ich die grundlegende boolesche (nicht-retrow) Methode und stellte meine Unterroutinen in boolesche Funktionen um. (PS: Ich wechsle zurück zur booleschen Methode, basierend auf der Antwort.)

Ich brauche Anleitung, wie man Funktionen in dieses Schema einpasst. Ich möchte, dass sie ihre realen Werte (eine Zeichenfolge oder ein Doppelwort, z. B. -1, wenn sie in einigen Fällen fehlschlagen) zurückgeben, damit ich sie in andere Funktionen verschachteln kann und nicht nur einen Fehler zurückgeben kann, der boolean behandelt.

So würde ein typischer Subroutinenaufruf zu bDrawCellBorders (myWS) innerhalb eines Einstiegspunktes aussehen. Unterrufe scheinen gut zu funktionieren. (Das heißt, es ist ein Unterprogramm, das in eine Funktion nur dann eingeschaltet wurde, so dass es einen boolean auf die Fehlerbehandlung Schema zurückkehren kann.)

Sub UpdateMe() ' Entry Point 

    Const sSOURCE As String = "UpdateMe()" 

    On Error GoTo ErrorHandler 

    Set myWS = ActiveCell.Worksheet 
    Set myRange = ActiveCell 
    myWS.Unprotect 

' lots of code 

    If Not bDrawCellBorders(myWS) Then ERR.Raise glHANDLED_ERROR ' Call subroutine 

' lots of code 

ErrorExit: 
    On Error Resume Next 
    Application.EnableEvents = True 
    myWS.Protect AllowFormattingColumns:=True 
    Exit Sub 

ErrorHandler: 
    If bCentralErrorHandler(msMODULE, sSOURCE,,True) Then ' Call as Entry Point 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 
End Sub 

aber ich weiß nicht, wie dies zu realen Funktionen zu erweitern. Dies basiert auf einem Beispiel in dem Buch, das für ein Unterprogramm erstellt wurde, und ich habe es nur auf eine Funktion umgeschaltet. Fragen: * Wie nenne ich es? Ist es einfach wie x = sngDoSomeMath (17) * Wird seine Fehlerbehandlung richtig funktionieren? * Wo ist der richtige Ort oder die richtigen Stellen, um die Fehlerbehandlungsroutine mit bReThrow = true aufzurufen?

Public Function sngDoSomeMath(ByVal iNum As Integer) As Single 

Dim sngResult As Single 

Const sSOURCE As String = "sngDoSomeMath()" 

On Error GoTo ErrorHandler 

' example 1, input did not pass validation. don't want to 
' go up the error stack but just inform the 
' calling program that they didn't get a good result from this 
' function call so they can do something else 
If iNum <> 42 Then 
    sngResult = -1 'function failed because I only like the number 42 
    GoTo ExitHere 
End If 

' example 2, true error generated 
sngResult = iNum/0 

sngDoSomeMath = lResult 

ExitHere: 
    Exit Function 
ErrorHandler: 

' Run cleanup code 
' ... here if any 

' Then do error handling 

If bCentralErrorHandler(msMODULE, sSOURCE, , , True) Then ' The true is for RETHROW 
    Stop 
    Resume 
End If 

End Function 

Der Error Handler Routine:

' 
' Description: This module contains the central error 
'    handler and related constant declarations. 
' 
' Authors:  Rob Bovey, www.appspro.com 
'    Stephen Bullen, www.oaltd.co.uk 
' 
' Chapter Change Overview 
' Ch# Comment 
' -------------------------------------------------------------- 
' 15 Initial version 
' 
Option Explicit 
Option Private Module 

' ************************************************************** 
' Global Constant Declarations Follow 
' ************************************************************** 
Public Const gbDEBUG_MODE As Boolean = False ' True enables debug mode, False disables it. 
Public Const glHANDLED_ERROR As Long = 9999  ' Run-time error number for our custom errors. 
Public Const glUSER_CANCEL As Long = 18   ' The error number generated when the user cancels program execution. 


' ************************************************************** 
' Module Constant Declarations Follow 
' ************************************************************** 
Private Const msSILENT_ERROR As String = "UserCancel" ' Used by the central error handler to bail out silently on user cancel. 
Private Const msFILE_ERROR_LOG As String = "Error.log" ' The name of the file where error messages will be logged to. 


'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 
' Comments: This is the central error handling procedure for the 
'   program. It logs and displays any run-time errors 
'   that occur during program execution. 
' 
' Arguments: sModule   The module in which the error occured. 
'    sProc   The procedure in which the error occured. 
'    sFile   (Optional) For multiple-workbook 
'        projects this is the name of the 
'        workbook in which the error occured. 
'    bEntryPoint  (Optional) True if this call is 
'        being made from an entry point 
'        procedure. If so, an error message 
'        will be displayed to the user. 
' 
' Returns:  Boolean   True if the program is in debug 
'        mode, False if it is not. 
' 
' Date   Developer  Chap Action 
' -------------------------------------------------------------- 
' 03/30/08  Rob Bovey  Ch15 Initial version 
' 
Public Function bCentralErrorHandler(_ 
     ByVal sModule As String, _ 
     ByVal sProc As String, _ 
     Optional ByVal sFile As String, _ 
     Optional ByVal bEntryPoint As Boolean, _ 
     Optional ByVal bReThrow As Boolean = True) As Boolean 

    Static sErrMsg As String 

    Dim iFile As Integer 
    Dim lErrNum As Long 
    Dim sFullSource As String 
    Dim sPath As String 
    Dim sLogText As String 

    ' Grab the error info before it's cleared by 
    ' On Error Resume Next below. 
    lErrNum = ERR.Number 
    ' If this is a user cancel, set the silent error flag 
    ' message. This will cause the error to be ignored. 
    If lErrNum = glUSER_CANCEL Then sErrMsg = msSILENT_ERROR 
    ' If this is the originating error, the static error 
    ' message variable will be empty. In that case, store 
    ' the originating error message in the static variable. 
    If Len(sErrMsg) = 0 Then sErrMsg = ERR.Description 

    ' We cannot allow errors in the central error handler. 
    On Error Resume Next 

    ' Load the default filename if required. 
    If Len(sFile) = 0 Then sFile = ThisWorkbook.Name 

    ' Get the application directory. 
    sPath = ThisWorkbook.Path 
    If Right$(sPath, 1) <> "\" Then sPath = sPath & "\" 

    ' Construct the fully-qualified error source name. 
    sFullSource = "[" & sFile & "]" & sModule & "." & sProc 

    ' Create the error text to be logged. 
    sLogText = " " & sFullSource & ", Error " & _ 
       CStr(lErrNum) & ": " & sErrMsg 

    ' Open the log file, write out the error information and 
    ' close the log file. 
    iFile = FreeFile() 
    Open sPath & msFILE_ERROR_LOG For Append As #iFile 
    Print #iFile, Format$(Now(), "mm/dd/yy hh:mm:ss"); sLogText 
    If bEntryPoint Or Not bReThrow Then Print #iFile, 
    Close #iFile 

    ' Do not display or debug silent errors. 
    If sErrMsg <> msSILENT_ERROR Then 

     ' Show the error message when we reach the entry point 
     ' procedure or immediately if we are in debug mode. 
     If bEntryPoint Or gbDEBUG_MODE Then 
      Application.ScreenUpdating = True 
      MsgBox sErrMsg, vbCritical, gsAPP_NAME 
      ' Clear the static error message variable once 
      ' we've reached the entry point so that we're ready 
      ' to handle the next error. 
      sErrMsg = vbNullString 
     End If 

     ' The return vale is the debug mode status. 
     bCentralErrorHandler = gbDEBUG_MODE 

    Else 
     ' If this is a silent error, clear the static error 
     ' message variable when we reach the entry point. 
     If bEntryPoint Then sErrMsg = vbNullString 
     bCentralErrorHandler = False 
    End If 

    'If we're using re-throw error handling, 
    'this is not the entry point and we're not debugging, 
    're-raise the error, to be caught in the next procedure 
    'up the call stack. 
    'Procedures that handle their own errors can call the 
    'central error handler with bReThrow = False to log the 
    'error, but not re-raise it. 
    If bReThrow Then 
     If Not bEntryPoint And Not gbDEBUG_MODE Then 
      On Error GoTo 0 
      ERR.Raise lErrNum, sFullSource, sErrMsg 
     End If 
    Else 
     'Error is being logged and handled, 
     'so clear the static error message variable 
     sErrMsg = vbNullString 
    End If 

End Function 

Antwort

4

Ich brauchte ein bisschen mehr Hilfe bei dieser speziellen Technik, also ging ich direkt zur Quelle und Mr. Bovey war gnädig genug, um zu antworten. Er gab mir die Erlaubnis, seine Antwort an die StackOverflow-Community zu senden.

Die folgenden Anweisungen beziehen sich auf seine bevorzugte Methode der Fehlerbehandlung für Funktionen die "boolesche Fehlerbehandlung" Technik und nicht auf die alternative "Methode erneut zu starten", beide in seinem Buch "Professional Excel Development" 2nd Edition beschrieben.


Hallo Shari,

In Antwort auf Ihre Fragen über Fehler in Funktionen der Handhabung gibt es drei Fehlerbehandlung Szenarien können Sie mit einer Funktion in VBA haben:

1) Die Funktion ist so trivial, das ist kein Fehlerhandler. In dem unwahrscheinlichen Ereignis tritt ein Fehler in einer Funktion wie diesem auf, die über in den Fehlerhandler der aufrufenden Prozedur überläuft.

2) Eine nicht-triviale Funktion benötigt einen Fehlerhandler und verwendet das in dem Buch beschriebene boolesche Rückgabewert . Alle anderen Werte, die die Funktion return benötigt, werden über ByRef-Argumente zurückgegeben. Dieser Fall deckt die überwiegende Mehrheit der Funktionen ab, die ich schreibe. Es gibt einige Dinge, die Sie nicht mit Funktionen wie dies tun können, füttern sie direkt in das Argument einer anderen Funktion ist ein Beispiel, aber ich halte dies für einen guten Kompromiss, um Kugel Beweis Fehlerbehandlung zu erreichen.

3) Eine nicht-triviale Funktion benötigt einen Fehlerbehandler und muss einen Wert zurückgeben, der nicht ist, bezogen auf seinen Fehlerstatus. Dies ist eine seltene Situation, weil ich 99% Plus davon in Fall 2 umwandeln kann, indem ich meinen Code umstrukturiere. Wenn Sie dies nicht tun können, ist Ihre einzige Wahl, einen beliebigen Rückgabewert auszuwählen, der außerhalb des Bereichs des Bereichs der normalen Rückgabewerte liegt, und damit anzugeben, dass ein Fehler aufgetreten ist. Wenn der Aufrufer dieser Funktion dieses willkürliche Fehlerflag Wert sieht, weiß es, dass es nicht fortgesetzt werden kann.

Rob Bovey Anwendung Professionals http://www.appspro.com/


Codebeispiel (Shari W)


' Show how to call a function using this error handling method. 
Const giBAD_RESULT As Integer = -1 

Function TestMath() ' An Entry Point 

    Dim sngResult As Single 
    Dim iNum As Integer 

    ' Call the function, actual result goes in sngResult but it returns the error handling boolean. 
    ' A true error like Div 0 will go to error handler. 

    ' Set Up Error Handling for Entry Point 
    Application.EnableCancelKey = xlErrorHandler 
    Dim bUserCancel As Boolean 
    Const sSOURCE As String = "TestMath()" 
    On Error GoTo ErrorHandler 
    ' End Error Set Up 

    iNum = 0 ' Try 0 to create error 
    If Not bDoSomeMath(iNum, sngResult) Then ERR.Raise glHANDLED_ERROR 
    ' If function does parameter checking and wants to return a bad input code, check for that. 
    If sngResult = giBAD_RESULT Then 
     MsgBox ("Bad input to bDoSomeMath " & iNum) 
    Else 
     MsgBox ("I believe the answer is " & sngResult) 
    End If 

ErrorExit: 
    On Error Resume Next 
    Exit Function 

ErrorHandler: 
    If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 
End Function 
Function bDoSomeMath(ByVal iNum As Integer, ByRef sngResult As Single) As Boolean 

    ' Error handling Set Up 
    Dim bReturn As Boolean 
    Const sSOURCE As String = "bDoSomeMath()" 
    On Error GoTo ErrorHandler 
    bReturn = True 
    ' End Error Set Up 

    If iNum < 0 Or iNum > 1000 Then 
     sngResult = giBAD_RESULT 'function failed because I only like the numbers 0 to 1000 
     GoTo ErrorExit 
    Else 
     sngResult = 100/iNum ' generate a true error by iNum = 0 
    End If 

ErrorExit: 
    On Error Resume Next 
    bDoSomeMath = bReturn 
    Exit Function 

ErrorHandler: 
    bReturn = False 
    If bCentralErrorHandler(msMODULE, sSOURCE, , , True) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 

End Function 
2

ein Vorschlag für Fehler in VBA Handling-Management kann here finden.

Das gleiche Werkzeug (MZ-Tools) und Methode (Standard/generische Fehlerbehandlung, die verwendet werden könnte, um ein automatisiertes Fehlermeldesystem zu erstellen) wird mit Excel funktionieren.

+0

MZ-Tools ist ein großartiges Werkzeug. Ich ersetzte ihren einfachen Standard ErrorHandler durch Rob Bovey's Version (oben). Mit ONE CLICK kann ich Header und Fehlerhandler in eine Routine einfügen, auch wenn ich es versäumt habe, mit ihnen zu beginnen.Aber ich brauche genauere Informationen zur Fehlerbehandlung für Funktionen! Vielen Dank. –

+0

+ 1 Guter Vorschlag zu MZ Tools :) –

+0

Danke Leute, aber ich hoffe immer noch, dass jemand, der die beschriebenen Methoden benutzt, mich anrempelt und mich glättet! Die Beispiele auf dieser Website und die, die mit MZ-Tools geliefert werden, sind zu einfach (ich habe sie bereits in den MZ-Tools-Optionen ersetzt.) –

12

Das ist ein erstaunliches Buch von Rob.

Meine zwei Cent von Fehlerbehandlung (entweder für eine Prozedur oder eine Funktion) basiert auf KISS (Keep it simple Dumme)

Verstehen Sie, was erwarten Sie von Ihrem Fehlerhandler wollen?

Dies ist in der Regel, was ich will/aus meinem Fehler-Handler erwarten ...

  1. Linie, auf der der Fehler
  2. Fehlernummer
  3. Fehlermeldung
  4. Reset-Ereignisse ggf.
  5. passiert

Lässt das oben genannte brechen. Da Sie bereits wissen, wie Ihr Fehlerhandler aussieht, betrachten Sie dieses Beispiel.

Sub Sample() 
    Dim i As Integer, j As Integer 

    On Error GoTo Whoa 

    Application.ScreenUpdating = False 

    i = 1111111111 

    For j = 1 To i 
     Debug.Print ThisWorkbook.Sheets(1).Cells(i, 1).Value 
    Next i 

LetsContinue: 
    Exit Sub 
Whoa: 
    MsgBox Err.Description 
    Resume LetsContinue 
End Sub 

Dies ist eine sehr einfache Fehlerbehandlung, aber es ist von sehr weniger Hilfe für mich. Lass es uns jetzt optimieren, um es nützlicher zu machen. Wenn Sie den obigen Code ausführen, erhalten Sie eine Fehlermeldung wie im Screenshot unten gezeigt und wenn Sie bemerken, ist es nicht viel hilfreich.

enter image description here

Lassen Sie uns nun alle Punkte angehen, dass ich in der Logic oben

erwähnt
  1. Linie, auf der der Fehler
  2. passiert

Es gibt eine Eigenschaft ERL genannt ist was sehr wenige Leute wissen. Sie können es tatsächlich verwenden, um die Zeilennummer des Codes zu erhalten, an dem der Fehler aufgetreten ist. Dafür müssen Sie sicherstellen, dass Sie Ihren Code nummerieren. Siehe dieses Beispiel.

Sub Sample() 
    Dim i As Integer, j As Integer 

10  On Error GoTo Whoa 

20  Application.ScreenUpdating = False 

30  i = 1111111111 

40  For j = 1 To i 
50   Debug.Print ThisWorkbook.Sheets(1).Cells(i, 1).Value 
60  Next j 

LetsContinue: 
70  Exit Sub 
Whoa: 
80  MsgBox Erl 
90  Resume LetsContinue 
End Sub 

Wenn Sie den obigen Code ausführen, erhalten Sie diese

enter image description here

So, jetzt weiß ich, dass der Fehler auf der Linie 30 passiert, die i = 1111111111 ist

zum nächsten zu bewegen

  1. Fehler Numb er
  2. Fehlermeldung

Die Fehlernummer und die Fehlermeldung kann von Err.Number und Err.Description bzw. abgerufen werden. So, jetzt lassen Sie uns kombinieren Erl, Err.Number und Err.Description

prüfen dieses Beispiel

Sub Sample() 
    Dim i As Integer, j As Integer 

10  On Error GoTo Whoa 

20  Application.ScreenUpdating = False 

30  i = 1111111111 

40  For j = 1 To i 
50   Debug.Print ThisWorkbook.Sheets(1).Cells(i, 1).Value 
60  Next j 

LetsContinue: 
70  Exit Sub 
Whoa: 
80  MsgBox "The Error Happened on Line : " & Erl & vbNewLine & _ 
      "Error Message : " & Err.Description & vbNewLine & _ 
      "Error Number : " & Err.Number 
90  Resume LetsContinue 
End Sub 

Wenn Sie diesen Code ausführen, werden Sie so etwas wie dieses.

enter image description here

Sie können wählen, um die Fehlermeldung anpassen, um die Nutzerfreundlichkeit zu machen. Zum Beispiel

'~~> Message you want to deliver to the user in case the error happens 
Const sMsg As String = "Please take a screenshot of this message and contact the developer for a resolution" 
'~~> Title of your message box 
Const sTitle As String = "Oopsie Daisies" 

'~~> Change the above as applicable 

Sub Sample() 
    Dim i As Integer, j As Integer 

10  On Error GoTo Whoa 

20  Application.ScreenUpdating = False 

30  i = 1111111111 

40  For j = 1 To i 
50   Debug.Print ThisWorkbook.Sheets(1).Cells(i, 1).Value 
60  Next j 

LetsContinue: 
70  Exit Sub 
Whoa: 
80  MsgBox "The Error Happened on Line : " & Erl & vbNewLine & _ 
      "Error Message : " & Err.Description & vbNewLine & _ 
      "Error Number : " & Err.Number & vbNewLine & vbNewLine & _ 
      sMsg, vbCritical, sTitle 
90  Resume LetsContinue 
End Sub 

enter image description here

Auf den nächsten :)

Reset-Ereignisse ggf.

Wenn Sie arbeiten mit Ereignissen und ein Fehler auftritt, wenn es ist keine Fehlerbehandlung, der Code bricht. Leider werden dadurch die Ereignisse nicht zurückgesetzt. Es ist sehr wichtig, dass Sie die Ereignisse im Error-Handler zurücksetzen.

Wenn Sie im obigen Code bemerken wir setzen die Application.ScreenUpdating = False. Wenn der Code bricht, wird dieses Ereignis nicht zurückgesetzt. Das müssen Sie in diesem Fall im Error-Handler LetsContinue behandeln. Siehe dieses Beispiel.

Wie Philippe, empfehle ich auch stark, dass Sie MZ-Tools für VBA verwenden. Ich benutze es jetzt für Eseljahre ...

Hoffe, dass dies hilft.

+2

interessante Antwort. Wenn ich wo Sie wäre, würde ich ein Fehler-Objekt und eine error.add-Methode implementieren, die die Fehler automatisch zu einer TXT-Datei, einer Datenbank oder sogar einer Mail hinzufügt. Dies vermeidet den Schritt "Screenshot" und ermöglicht es Ihnen, Ihre Debug-Aufgaben wie ein Profi zu verwalten! –

+0

True :) Und ich dachte an die E-Mail-Option, aber ich war mir nicht sicher, ob der Benutzer E-Mail-Zugriff auf diesen PC haben könnte. Ich bin sicher, was Sie vorgeschlagen haben, kann leicht integriert werden, wenn der Benutzer will. Mein vorstehender Vorschlag dreht sich um 'KISS'. Ein Benutzer kann den obigen Vorschlag nehmen und es zu einem viel höheren Niveau bringen :) –

+0

Siddharth - Sie sind eine Kraft, mit der auf dieser Website gerechnet werden muss! Danke, dass du so vielen geholfen hast. Ihre Antwort ist eine ausgezeichnete Lösung für ein grundlegendes Fehlerüberprüfungsmodell, und viele Leute, die sich damit beschäftigen, werden nicht weiter gehen müssen. Ich brauche jedoch etwas robusteres für meinen Firmenkunden, weshalb ich versuche, die feinen Punkte der Fehlerbehandlungstechniken von Bovey zu verstehen. Ich mag die Idee, dass das Fehlerprotokoll mir per E-Mail gesendet wird. –