I frequently encounter this situation in my VB6 applications
Private Sub DoSomething
On Error Goto err1
Call ProcessLargeBatch1
Call ProcessLargeBatch2
'... more ...'
Exit Sub
err1:
Call Cleanup 'Specific for DoSomething'
Call HandleError 'General error handling: Logging, message box, ...'
End Sub
The Cleanup procedure sometimes reverts actions, rolls back a transaction, deletes temporary files, and so on. In most cases this operation can also fail.
What do I do in this case? I'd add an On Error Resume Next
into the error handler but that deletes the existing Err
object. Adding an error handler to Cleanup
has the same problem.
What is the best way to ensure that the original errors still gets processed/logged?
EDIT: One additional problem is that I also want to notify the user of the error. Sometimes it is important, that the cleanup happens fast and I don't want the message box block the application for a long time and do the cleanup after the user acknowledges the error.
Firstly, read all the information out of the Err object that you will need, i.e. number, description, etc., then clear the error and do what you want.
Change the way you inform the user to use the values you have cached, and not to use the Err object itself.
From your example you are doing the cleanup properly. Your HandleError should only log the error not do any UI. The UI is handled up at the form level.
What you need to when an error occurs is
- Clean Up
- Log the Error
- Raise the Error Again via Err.Raise
This will work it's way up the call stack to the event that called the original code. Then the sequence will become
- Clean Up
- Log the Error
- Display the the Error Notification Dialog
Note that your Error Logging can be intelligent in that subsequent logs of the same error can just add to the recorded call stack.
You want to make sure that EVERY event has a error handler. Not every procedures needs one but definitely every event. Unhandled errors in a event will cause a VB6 application to shut down unexpectedly.
If I can handle all errors in one place, I'll usually put in into a structure something like this:
Public Sub SubThatShouldHandleErrors()
Const ROUTINE_NAME = "SubThatShouldHandleErrors"
On Error Goto Catch
' "normal" processing here...
Finally:
' non-error case falls through to here
' perform clean-up that must happen even when an error occurred
On Error Goto 0 ' reset: not really needed any more, but it makes me feel more comfortable
Exit Sub
Catch:
' Error handling here, I may have logging that uses ROUTINE_NAME
Resume Finally
End Sub
If I need more than one error-handler, I'll try very hard to restructure my code to make that not be the case but if absolutely necessary I'll write a custom handler; my template is only a guideline.
Log your error first. Then do an On Error Resume Next. Have your cleanup encapsulated in methods that have their own error handling. This should be yourbest bet.
I really really don't like error handlers. This is what I do;
- Create a Error class or module that contains all properties that the built in one contains, as well as a CopyError-method that fills these properties from the err-object.
- Watch for errors where they can appear:
.
' lots of code that will probably work
On Error Resume Next
Open "c:\filethatdoesntexist.txt" For Input As #1
Error.CopyError
On Error Goto 0
Select Case Error.Number
Case 53'File doesn't exist
' handle that error here
Case 0
' no error
Case Else
' Just throw the error on
Err.Raise Error.Number, Error.Description, ...
End Select
' more code that will probably work