SimplyVBA and SimplyVB6 Global Error Handler Manual v1.4QuickStart: Sample.mdb (VBA) 26th May 2009: v1.4 update now available from download page. Update now for some great new features. On this page you'll find:OverviewWe understand that most developers using the SimplyVBA GLobal Error Handler will want two distinct error dialogs: - One for developing that includes debugging options. We call this the developer error dialog.
- One for when releasing your application to your clients. We call this the release error dialog.
With the VBA Global Error Handling system you get many options when deciding what kind of dialog to use; - The new customizable Vista-style dialog

- A fully customizable Access form

- Simple MsgBox or any other programmable interface

We provide you with a sample database application that is designed to show you how to implement error dialogs into your applications. In particular, the sample covers using both the Vista-style dialog and an Access form for both 'release' and 'developer' scenarios. The example formWhen you open the demo application, you will be presented with the demo form: 
To implement this form, we have designed four separate (and re-usable) global error handling subroutines which are defined in the VBA module ModGlobalErrorHandler: Public Sub MyGlobalErrorHandler_Release()
Select Case ErrEx.State
Case OnErrorGoto0
' ---------------------------------------------------------------
' Unhandled errors
' ---------------------------------------------------------------
LogErrorToTable
ErrEx.State = ErrEx.ShowErrorDialog
Case OnErrorGotoLabel
' ---------------------------------------------------------------
' Ignore locally handled errors
' ---------------------------------------------------------------
Case OnErrorPropagate
' ---------------------------------------------------------------
' Ignore locally handled errors
' (handled by a previous routine in the call stack)
' ---------------------------------------------------------------
Case OnErrorResumeNext
' ---------------------------------------------------------------
' Ignore errors when On Error Resume Next is set
' ---------------------------------------------------------------
Case CalledByLocalHandler
' ---------------------------------------------------------------
' ErrEx.CallGlobalErrorHandler was called
'
' This is a special case for when local error handling was in use
' but the local error handler has not dealt with the error and
' so has passed it on to the global error handler
' ---------------------------------------------------------------
LogErrorToTable
ErrEx.State = ErrEx.ShowErrorDialog
' ---------------------------------------------------------------
' NOTE:
'
' Since this global error handler routine is for 'release', not
' 'development', we don't really want to end abruptly here.
'
' So instead, we set ErrEx.State = CalledByLocalHandler
' which ensures we resume after the CallGlobalErrorHandler
' line - where cleanup code can then be done
' ---------------------------------------------------------------
ErrEx.State = CalledByLocalHandler
End Select
End Sub
<< Hide full codePublic Sub MyGlobalErrorHandler_Developer()
Select Case ErrEx.State
Case OnErrorGoto0
' ---------------------------------------------------------------
' Unhandled errors
' ---------------------------------------------------------------
LogErrorToTable
ErrEx.State = ErrEx.ShowErrorDialog
Case OnErrorGotoLabel
' ---------------------------------------------------------------
' Ignore locally handled errors
' ---------------------------------------------------------------
Case OnErrorPropagate
' ---------------------------------------------------------------
' Ignore locally handled errors
' (handled by a previous routine in the call stack)
' ---------------------------------------------------------------
Case OnErrorResumeNext
' ---------------------------------------------------------------
' Ignore errors when On Error Resume Next is set
' ---------------------------------------------------------------
Case CalledByLocalHandler
' ---------------------------------------------------------------
' ErrEx.CallGlobalErrorHandler was called
'
' This is a special case for when local error handling was in use
' but the local error handler has not dealt with the error and
' so has passed it on to the global error handler
' ---------------------------------------------------------------
LogErrorToTable
ErrEx.State = ErrEx.ShowErrorDialog
End Select
End Sub
<< Hide full code
Public Sub MyGlobalErrorHandler_Release_AccessForm()
Select Case ErrEx.State
Case OnErrorGoto0
' ---------------------------------------------------------------
' Unhandled errors
' ---------------------------------------------------------------
LogErrorToTable
DoCmd.OpenForm "CustomErrorForm_Release", acNormal, , , , acDialog
Case OnErrorGotoLabel
' ---------------------------------------------------------------
' Ignore locally handled errors
' ---------------------------------------------------------------
Case OnErrorPropagate
' ---------------------------------------------------------------
' Ignore locally handled errors
' (handled by a previous routine in the call stack)
' ---------------------------------------------------------------
Case OnErrorResumeNext
' ---------------------------------------------------------------
' Ignore errors when On Error Resume Next is set
' ---------------------------------------------------------------
Case CalledByLocalHandler
' ---------------------------------------------------------------
' ErrEx.CallGlobalErrorHandler was called
'
' This is a special case for when local error handling was in use
' but the local error handler has not dealt with the error and
' so has passed it on to the global error handler
' ---------------------------------------------------------------
LogErrorToTable
DoCmd.OpenForm "CustomErrorForm_Release", acNormal, , , , acDialog
' ---------------------------------------------------------------
' NOTE:
'
' Since this global error handler routine is for 'release', not
' 'development', we don't really want to end abruptly here.
'
' So instead, we set ErrEx.State = CalledByLocalHandler
' which ensures we resume after the CallGlobalErrorHandler
' line - where cleanup code can then be done
' ---------------------------------------------------------------
ErrEx.State = CalledByLocalHandler
End Select
End Sub
<< Hide full code
Public Sub MyGlobalErrorHandler_Developer_AccessForm()
Select Case ErrEx.State
Case OnErrorGoto0
' ---------------------------------------------------------------
' Unhandled errors
' ---------------------------------------------------------------
LogErrorToTable
DoCmd.OpenForm "CustomErrorForm_Developer", acNormal, , , , acDialog
Case OnErrorGotoLabel
' ---------------------------------------------------------------
' Ignore locally handled errors
' ---------------------------------------------------------------
Case OnErrorPropagate
' ---------------------------------------------------------------
' Ignore locally handled errors
' (handled by a previous routine in the call stack)
' ---------------------------------------------------------------
Case OnErrorResumeNext
' ---------------------------------------------------------------
' Ignore errors when On Error Resume Next is set
' ---------------------------------------------------------------
Case CalledByLocalHandler
' ---------------------------------------------------------------
' ErrEx.CallGlobalErrorHandler was called
'
' This is a special case for when local error handling was in use
' but the local error handler has not dealt with the error and
' so has passed it on to the global error handler
' ---------------------------------------------------------------
LogErrorToTable
DoCmd.OpenForm "CustomErrorForm_Developer", acNormal, , , , acDialog
End Select
End Sub
<< Hide full code When you click on any of these options on the form, the EnableErrorHandler subroutine (also defined in ModGlobalErrorHandler) chooses the corresponding global error handler subroutine name and calls ErrEx.EnableGlobalErrorHandler to activate the subroutine as the global error handler. For the two Vista-dialog options, the vista dialog is set up here with a call to SetupErrorDialog_Release or SetupErrorDialog_Developer as appropriate (these routines customize the Vista-dialog).
Although four options are given for demonstration purposes, typically you would only implement two of these - one 'release' and one for yourself, the 'developer'. It is also important to realise that in a real world application you will probably want to set the global error handler only once - usually in a subroutine that is called from the applications AutoExec macro (using the RunCode macro command). The error handler routines implementationGlobal error handling routines can be implemented in a variety of ways. In the Sample.mdb they have been implemented in the most common way as it lends itself most closely to the way developers usually implement normal VBA error handling. Each of the four of our global error handlers have been implemented as follows; - They ignore errors that are caught in a 'On Error Resume Next' statement
- They ignore errors that are caught in a 'On Error Goto Label' statement
- They have a special case for handling errors passed on from a local error handler (ErrEx.State = CalledByLocalHandler)
- They log error details to a table, via a subroutine called LogErrorToTable
To give a better understanding, we'll now look at the error simulators on our demo form. Tip: Along with these details below, it is highly recommended to step through the demo code line by line so that you can better understand the program flow. Demo error 1: 'Basic open form error' button
Private Sub btnOpenFormError_Click()
' We won't bother with local error handling here. Our global error handler will handle it nicely.
' The form ABC does not exist, so will produce an error.
DoCmd.OpenForm "ABC"
' A lot of simple procedures can be implemented without any local error handling,
' which makes code maintenance much easier and reduces code size too.
' Normally we'd never get here, but if we are using the 'developer' error dialog
' then the user can select 'Ignore & Continue', and we will resume here...
MsgBox "btnOpenFormError_Click: We must have resumed after the error."
End Sub
In the example above, if we're using MyGlobalErrorHandler_Release, then after the user clicks 'Close' to the error, ErrEx.State is assigned the value of OnErrorEnd, and then program flow is stopped. However, if we're using MyGlobalErrorHandler_Developer, then the error dialog has been setup to have extra buttons ('debug' and 'ignore & continue') - the error dialogs have been previously setup in SetupErrorDialog_Release and SetupErrorDialog_Developer respectively. If the user selects 'Ignore & Continue' (OnErrorResumeNext) then this acts as if the developer had put On Error Resume Next just before the error occurred. If the user selects 'End' (OnErrorEnd) then this acts as if the developer pressed the 'End' button on the old VBE error dialog. In other words, the program flow will stop immediately after our error handler has returned. If the user selects 'Debug' (OnErrorDebug) then this acts as if the developer has pressed the 'Debug' button on the old VBE error dialog. In other words, the VBE editor opens and highlights the line of error. Demo error 2: 'Recordset error' button
Private Sub btnRecordsetError_Click()
Dim rs As DAO.Recordset
' Upon encountering an error we want to ensure the recordset object has definitely been released.
' In order to do this, we use a small local error handler that will pass any errors
' on to the global error handler with a flag set to ensure that the global error handler
' will display the error dialog (otherwise it would just ignore the error because of the way it
' is implemented in this example). Step through the code to grasp a better understanding.
On Error GoTo LocalErrorHandler
' Customers table does not exist, so will cause an error.
Set rs = CurrentDb.OpenRecordset("SELECT * FROM CUSTOMERS")
' Normally we'd never get here, but if we are using the 'developer' error dialog
' then the user can select 'Ignore & Continue', and we will continue...
MsgBox "btnRecordsetError_Click: We must have resumed after the error."
Cleanup:
On Error Resume Next ' Ignore errors when cleaning up.
rs.Close
Set rs = Nothing
Exit Sub
LocalErrorHandler:
ErrEx.CallGlobalErrorHandler ' Call the global error handler to deal with unhandled errors
Resume Cleanup ' After showing the error dialog, we will clean up
End Sub
- --- The program flow upon error (assume MyGlobalErrorHandler_Release is selected) ---
When the CurrentDb.OpenRecordset line causes an error, our MyGlobalErrorHandler_Release routine gets called.
- ErrEx.State is OnErrorGotoLabel as 'On Error GoTo LocalErrorHandler' was set in the btnRecordsetError_Click procedure
Case OnErrorGotoLabel
' ---------------------------------------------------------------
' Ignore locally handled errors
' ---------------------------------------------------------------
- --- Program flow now returns to LocalErrorHandler label in the local procedure: ---
LocalErrorHandler:
' Pass the error on to our global error handler
ErrEx.CallGlobalErrorHandler ' Call the global error handler to deal with unhandled errors
Resume Cleanup ' After showing the error dialog, we will clean upIn this example, we wanted to: - show the error details to the user,
- ensure the Cleanup routine is always called.
To do this, our local error handler calls ErrEx.CallGlobalErrorHandler which then calls our global error handler MyGlobalErrorHandler_Release again but with a special ErrEx.State of CalledByLocalHandler ...
- --- Program flow now returns to MyGlobalErrorHandler_Release: ---
Case CalledByLocalHandler
' ---------------------------------------------------------------
' ErrEx.CallGlobalErrorHandler was called
'
' This is a special case for when local error handling was in use
' but the local error handler has not dealt with the error and
' so has passed it on to the global error handler
' ---------------------------------------------------------------
LogErrorToTable
ErrEx.State = ErrEx.ShowErrorDialog
' ---------------------------------------------------------------
' NOTE:
'
' Since this global error handler routine is for 'release', not
' 'development', we don't really want to end abruptly here.
'
' So instead, we set ErrEx.State = CalledByLocalHandler
' which ensures we resume after the CallGlobalErrorHandler
' line - where cleanup code can then be done
' ---------------------------------------------------------------
ErrEx.State = CalledByLocalHandlerThis adds error details to the error log table and shows the Vista-style error dialog to the user.
Since MyGlobalErrorHandler_Release is designed for release purposes, we also ensure that ErrEx.State is set to CalledByLocalHandler when we exit - this causes the code following the ErrEx.ErrEx.CallGlobalErrorHandler (in the local procedure) to continue - allowing us to cleanup any potentially open recordset objects nicely...
- --- Program flow now returns to the line after ErrEx.ErrEx.CallGlobalErrorHandler in the local procedure: ---
Resume Cleanup
- --- Program flow now returns to Cleanup label in the local procedure: ---
Cleanup:
On Error Resume Next ' Ignore errors when cleaning up.
rs.Close
Set rs = Nothing
Exit SubJob done!
Demo error 3: 'ChangeProperty example' button
Private Sub btnChangePropertyExample_Click()
' Error handling is done in the subroutine.
ChangeProperty "ABC", dbText, "Blah"
End Sub
'ChangeProperty as defined in module ModPropertyExample
Public Function ChangeProperty(stPropName As String, PropType As DAO.DataTypeEnum, vPropVal As Variant) As Boolean
On Error GoTo LocalErrorHandler
Dim Db As DAO.Database
Dim Prop As DAO.Property
Set Db = CurrentDb
Db.Properties.Delete stPropName
Db.Properties.Delete stPropName ' This causes an error because the property doesn't already exist
Set Prop = Db.CreateProperty(stPropName, PropType, vPropVal, True)
Db.Properties.Append Prop
ChangeProperty = True
Cleanup:
On Error Resume Next ' Ignore errors when cleaning up.
Set Prop = Nothing
Set Db = Nothing
Exit Function
LocalErrorHandler:
' Handle local specific errors here:
Const conPropNotFoundError As Long = 3265
Select Case Err.Number
Case conPropNotFoundError
' We will ignore the 'property does not exist' error
Resume Next
End Select
ErrEx.CallGlobalErrorHandler ' Call the global error handler to deal with unhandled errors
Resume Cleanup ' After showing the error dialog, we will clean up
End Function
- --- The program flow upon error (assume MyGlobalErrorHandler_Release is selected) ---
When the 'Db.Properties.Delete stPropName' line causes an error, our MyGlobalErrorHandler_Release routine gets called.
- ErrEx.State is OnErrorGotoLabel as 'On Error GoTo LocalErrorHandler' was set in the btnRecordsetError_Click procedure
Case OnErrorGotoLabel
' ---------------------------------------------------------------
' Ignore locally handled errors
' ---------------------------------------------------------------
- --- Program flow now returns to LocalErrorHandler label in the local procedure: ---
LocalErrorHandler:
' Handle local specific errors here:
Const conPropNotFoundError As Long = 3265
Select Case Err.Number
Case conPropNotFoundError
' We will ignore the 'property does not exist' error
Resume Next
End Select
ErrEx.CallGlobalErrorHandler ' Call the global error handler to deal with unhandled errors
Resume Cleanup ' After showing the error dialog, we will clean up
In this example, we first wanted to handle the specific error of 'property does not exist', so we check that here.Notice that we use Err in local procedure handling, and ErrEx when in the global error handler, although in practice it doesn't make any difference as long as we always use ErrEx in the global error handler.
Case conPropNotFoundError This is true.
Resume Next This then puts program flow at the line after the error
Set Prop = Db.CreateProperty(stPropName, PropType, vPropVal, True) Program flow is then as expected - the property is created successfully and CleanUp continues.Notice that no error dialog is displayed in the example since it is an example of a commonly used way of trapping when a known error might occur.
Further reading / related pages: |