Public Const ErrorIgnore = vbObjectError + 513
Public Const ErrorNotice = vbObjectError + 514
Public Const ErrorData = vbObjectError + 515
Public Const ErrorFail = vbObjectError + 516
Public Enum ErrorHandlerActions
eaEnter
eaExit
eaLog
eaNotify
eaRaise
eaRoot
End Enum
' ErrorHandler()
' This function is designed to offer robust and flexible
' error handling with a minimum of impact to the bulk of
' your code. It allows you to handle errors in data
' modules, classes, or generic utilities seemlessly.
'
' Concept
' Use "On Error" trapping only in root-level functions
' (defined as any event procedure fired by a user
' interaction or timer control) and functions that require
' a close-down section, such as those that access a
' database or create objects. No other error trapping
' is required. (The root-level trapping will catch any
' subsequent errors down the call stack, so additional
' trapping would be redundant.)
'
' Requirements
' ErrorHandler() requires a public enumeration as follows:
'Public Enum ErrorHandlerActions
' eaEnter
' eaExit
' eaLog
' eaNotify
' eaRaise
' eaRoot
'End Enum
'
' Call Stack
' ErrorHandler() tracks the call stack as provided by you
' the programmer. Send the function/sub/property/method
' procedure name to ErrorHandler() as the first line of
' code in each procedure. You should include the module
' name as well; one simple idea is to define a private
' constant in all modules that contains the module name,
' and then add the function name in each individual call.
'
' Root-Level functions
' For root-level functions (which will have error trapping)
' add to the call stack using the eaRoot action. These are
' also the functions where user notification is handled by
' calling the eaNotify action in the error trapping
' routine. Note that the eaExit action (discussed later)
' isn't needed in root-level functions. For example:
'Public Sub List1_Click()
'On Error GoTo List1_ClickErr
' ErrorHandler eaRoot, ModuleConstant & ".List1_Click"
' ' Your code to process the event goes here
'
'List1_ClickExit:
' Exit Sub
'
'List1_ClickErr:
' ErrorHandler eaNotify
' Resume List1_ClickExit
'End Sub
'
' For "standard" functions, defined as being neither root-
' level nor requiring a close-down section, manage the
' call stack by beginning and ending each function with
' the eaEnter and eaExit actions respectively. Like the
' eaRoot action, eaEnter requires the function name. Also
' remember that error trapping is unnecessary in standard
' functions. Example:
'Public Sub Refresh()
' ErrorHandler eaEnter, ModuleConstant & ".Refresh"
' ' Your code goes here
' ErrorHandler eaExit
'End Sub
'
' In non-root-level functions that require a close-down
' section, include eaEnter and eaExit actions just like
' with standard functions, but there is additional support
' required. Your error trapping that forces a close-down
' section (releasing objects or closing recordsets) to fire
' will absorb the actual error, leaving nothing to pass
' back up the call stack for ErrorHandler() to use in
' notifying the user. This problem is made worse because
' errors are typically suppressed (via On Error Resume
' Next) during close-down sections.
'
' ErrorHandler() provides a simple solution for this
' dilemma. Send the eaLog action in the error trapping
' routine to log the error for later use. In your close-
' down code, after it is finished be sure error trapping
' is re-enabled (with On Error Goto 0) and send the
' eaRaise action to propagate the error back up the call
' stack. Here is a complete example:
'Public Function GetEmployeeName() As String
'On Error GoTo GetEmployeeNameErr
' ErrorHandler eaEnter, ModuleConstant & ".GetEmployeeName"
' ' Your code to access data/create objects goes here
' ErrorHandler eaExit
'
'GetEmployeeNameExit:
' On Error Resume Next
' ' Your code to close recordsets/objects goes here
' On Error GoTo 0
' ErrorHandler eaRaise
' Exit Function
'
'GetEmployeeNameErr:
' ErrorHandler eaLog
' Resume GetEmployeeNameExit
'End Function
'
' Custom errors
' Your program will no doubt either have custom errors, or
' at least specific custom messages to some default errors.
' Modify the Select Case statement to handle your specific
' errors as appropriate.
'
' Action Summary
' eaEnter
' Requires a source name. (Class/Module.Function)
' Used at start of all non-root-level functions.
' eaExit
' No source name required.
' Used at end of all non-root-level functions.
' eaLog
' No source name required.
' Used in error trapping routine when a close-down
' section is required. (Function uses data or objects.)
' eaNotify
' No source name required.
' Used in error trapping routine of root-level functions.
' eaRaise
' No source name required.
' Used at end of close-down sections.
' eaRoot
' Requires a source name. (Class/Module.Function)
' Used at start of all root-level functions.
Public Sub ErrorHandler(penAction As ErrorHandlerActions, Optional pstrSource As String)
Const Delimiter = vbCrLf & " "
Static slngNumber As Long
Static sstrSource As String
Static sstrDescription As String
Static sstrCallStack As String
Dim strMessage As String
Dim strDetail As String
' Clear error state if entering a root function
If penAction = eaRoot Then
Err.Clear
slngNumber = 0
sstrSource = ""
sstrDescription = ""
sstrCallStack = ""
End If
' Remove source from list if successfully exiting a function
If penAction = eaExit Then
If InStr(sstrCallStack, Delimiter) > 0 Then
sstrCallStack = Left(sstrCallStack, InStrRev(sstrCallStack, Delimiter, Len(sstrCallStack) - 1) - 1)
End If
Exit Sub
End If
' Log source
If penAction = eaRoot Or penAction = eaEnter Then
sstrCallStack = sstrCallStack & Delimiter & pstrSource
End If
' Log error number and description if in an error state
If Err.Number <> 0 Then ' If penAction = eaLog Or penAction = eaNotify Then
slngNumber = Err.Number
sstrSource = Err.Source
sstrDescription = Err.Description
End If
' Raise error
If penAction = eaRaise And slngNumber <> 0 Then
Err.Raise slngNumber, sstrSource, sstrDescription
End If
' Notify user
If penAction = eaNotify And slngNumber <> 0 Then
' Update the screen
Screen.MousePointer = vbNormal
'UnlockWindow
strDetail = _
"Number: " & slngNumber & " (" & slngNumber - vbObjectError & ")" & vbCrLf & _
"Source: " & sstrSource & vbCrLf & _
"Description: " & sstrDescription & vbCrLf & vbCrLf & _
"Call stack:" & sstrCallStack
' Show message
Select Case slngNumber
Case ErrorIgnore
Case ErrorNotice
MsgBox sstrDescription, vbInformation, "Notice"
Case Else ' Unexpected error
strMessage = sstrDescription & vbCrLf & vbCrLf & "View detail?"
If MsgBox(strMessage, vbYesNo + vbInformation + vbDefaultButton2, "Notice") = vbYes Then
Clipboard.Clear
Clipboard.SetText strMessage, vbCFText
strDetail = strDetail & vbCrLf & vbCrLf & "Detail has been copied to the clipboard."
MsgBox strDetail, vbExclamation, "Error Detail"
End If
End Select
End If
' Clear error state
If penAction = eaNotify Then
Err.Clear
slngNumber = 0
sstrCallStack = ""
sstrDescription = ""
sstrSource = ""
End If
End Sub