QuickStart: Sample.mdb (VBA)



 Sept 2010: v2 of VBA Global Error Handler is now available vbWatchdog (no DLLs!)

Overview

We understand that most developers using the SimplyVBA GLobal Error Handler will want two distinct error dialogs:

  1. One for developing that includes debugging options. We call this the developer error dialog.
  2. 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;

  1. The new customizable Vista-style dialog

    Vista Dialog Example

  2. A fully customizable Access form

    Access form error dialog example

  3. Simple MsgBox or any other programmable interface

    MsgBox error dialog example

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 form

We provide a Sample MDB Access application that demonstrates some of the main features of the error handler. The sample application is installed with our software - you can find it in the EverythingAccess.com folder of your Start menu.

When you open the demo application, you will be presented with the demo form:

Sample database Access 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()
>> Show full code
Public Sub MyGlobalErrorHandler_Developer()
>> Show full code
Public Sub MyGlobalErrorHandler_Release_AccessForm()
>> Show full code
Public Sub MyGlobalErrorHandler_Developer_AccessForm() 
>> Show 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 implementation

Global 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

  • --- The program flow upon error (assume MyGlobalErrorHandler_Release is selected) ---

    When the DoCmd.OpenForm line causes an error, our MyGlobalErrorHandler_Release routine gets called.
  • ErrEx.State is OnErrorGoto0 as no specific error handling was set in the btnOpenFormError_Click procedure.
    Case OnErrorGoto0
        ' ---------------------------------------------------------------
        ' Unhandled errors
        ' ---------------------------------------------------------------
        LogErrorToTable
        
        ErrEx.State = ErrEx.ShowErrorDialog
    This adds error details to the error log table and then shows the Vista error dialog to the user.

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 up

    In this example, we wanted to:

    1. show the error details to the user,
    2. 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 = CalledByLocalHandler
    This 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 Sub
    Job 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.