I got the book \"Professional Excel Development\" by Rob Bovey and it is opening up my eyes.
I am refitting my code with error handling. However, there is a lot I do
That is an amazing book by Rob.
My two cents of Error Handling (Either for a procedure or a Function) is based on KISS (Keep it simple Silly)
Understand what do you want from your error handler?
This is usually what I want/expect from my error handler...
Lets break the above. As you are by now already aware how your error handler looks like, Consider this example.
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
This is a very basic error handler but it's of very less help to me. So let's now tweak it to make it more useful. If you run the above code you get an error message like shown in the screenshot below and if you notice, it's not of much help.
Let's now tackle all the points that I mentioned in the Logic
above
- Line on which the error happened
There is a property called ERL
which very few people are aware of. You can actually use it to get the line number of the code where the error happened. For that you have to ensure you number your code. See this example.
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
When you run the above code, you will get this
So now I know that the error happened on Line 30 which is i = 1111111111
Moving on to next
- Error Number
- Error Message
The error number and the error message can be retrieved from Err.Number
and Err.Description
respectively. So now let's combine Erl
, Err.Number
and Err.Description
Check this example
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
When you run this code, you will get something like this.
You can choose to further customize the Error Message to make it more user friendly. For example
'~~> 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
On to the next one :)
Reset Events if applicable
When you are working with events and an error occurs, if there is no error handling, the code breaks. Unfortunately that doesn't reset the events. It is very important that you reset the events in the Error handler.
If you notice in the above code we are setting the Application.ScreenUpdating = False
. When the code breaks, that event doesn't get reset. You will have to handle that in the Error handler LetsContinue
in this case. See this example.
'~~> 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 Application.ScreenUpdating = True
80 Exit Sub
Whoa:
90 MsgBox "The Error Happened on Line : " & Erl & vbNewLine & _
"Error Message : " & Err.Description & vbNewLine & _
"Error Number : " & Err.Number & vbNewLine & vbNewLine & _
sMsg, vbCritical, sTitle
100 Resume LetsContinue
End Sub
Like Philippe, I also strongly suggest that you use MZ-Tools for VBA. I have been using it now for donkey years...
Hope this helps.
I needed a bit more help on this specific technique so I went right to the source and Mr. Bovey was gracious enough to reply. He gave me permission to post his response to the StackOverflow community.
The instructions below refer to his preferred method of error handling for functions the "boolean error handling" technique and not to the alternate "rethrow method", both described in his book "Professional Excel Development" 2nd edition.
Hi Shari,
In answer to your questions about error handling in functions, there are three error handling scenarios you can have with a function in VBA:
1) The function is so trivial that is doesn't need an error handler. In the unlikely event an error occurs in a function like this it will spill over into the error handler of the calling procedure.
2) A non-trivial function needs an error handler and uses the Boolean return value system described in the book. Any other values the function needs to return are returned through ByRef arguments. This case covers the vast majority of functions I write. There are some things you can't do with functions like this, feeding them directly into the argument of another function is one example, but I consider this a good tradeoff in order to achieve bullet proof error handling.
3) A non-trivial function needs an error handler and must return a value not related to its error status. This is a rare situation because I can convert 99% plus of these into case 2 by restructuring my code. If you can't do this, your only choice is to select an arbitrary return value that is out of the range of normal return values and use this to indicate that an error has occurred. If the caller of this function sees this arbitrary error flag value it knows it can't continue.
Rob Bovey Application Professionals http://www.appspro.com/
Code Example (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
a proposal for error handling management in VBA can be found here .
The very same tool (MZ-Tools) and method (standard/generic error handler, which could be used to build an automated error reporting system) will work with Excel.