Proper Error Trapping Pattern
-
I just wanted to share with you guys the proper way to enclose all VB6 subroutines in order to trap errors, according to my company. Like if you wanted, for example, to exit a form:
Private Sub Command212_Click()
On Error GoTo Err_Command212_Click
Me.Close
Exit_Command212_Click:
Exit Sub
Err_Command212_Click:
MsgBox Err.Description
Resume Exit_Command212_Click
End Sub
-
Don't be too hard on them: it is exactly what you get if look at the functions that MS Access creates itself.
But, yes, it's the first code up agains the wall when I edit one of those procedures.
-
It surprises me that the guidelines don't include something like:
@Publius said:
Private Sub Command212_Click()
On Error GoTo Err_Command212_Click
Dim myObject as SomeObject
Set myObject = new SomeObject()
'do something with myObject
Exit_Command212_Click:
Set SomeObject = Nothing
Exit Sub
Err_Command212_Click:
MsgBox Err.Description
Resume Exit_Command212_Click
End SubBecause THAT would be nasty :o)
And yes, that's the way it should be done at the company I work for.
-
They got so close to the Try-Catch-Finally pattern it isn't funny. One small change and they would have had it:
Private Sub Command212_Click()
On Error GoTo Err_Command212_Click
Me.Close
GoTo Cleanup
Err_Command212_Click:
MsgBox Err.DescriptionCleanup:
End Sub
This then works exactly like Try-Catch-Finally with still only one point of exit and the ability to clean up any local resources regardless of error. The one problem here is if you throw an error in cleanup, because then yuo get a never ending loop.
-
@KattMan said:
This then works exactly like Try-Catch-Finally with still only one point of exit and the ability to clean up any local resources regardless of error. The one problem here is if you throw an error in cleanup, because then yuo get a never ending loop.
You should be able to rectify that by putting an "on error goto 0" line immediately after the label to disable error handling if you want it to work like .NET. I could be wrong, of course. I haven't had to touch VB6 in years (thank goodness!) but I'm pretty sure you could disable error handling even after an error has occurred.
-
@KattMan said:
The way the pattern was written by Publius is correct (edit: except for missing the 'on error resume next' in the error handler) and already includes a cleanup block: between the label "Exit_Command212_Click:" and the statement "Exit Sub" he would have to close his database connections for example.They got so close to the Try-Catch-Finally pattern it isn't funny. One small change and they would have had it:
Private Sub Command212_Click()
On Error GoTo Err_Command212_Click
Me.Close
GoTo Cleanup
Err_Command212_Click:
MsgBox Err.DescriptionCleanup:
End Sub
This then works exactly like Try-Catch-Finally
By putting the clean-up block after the regular code and before the Error handling block it will always get executed after normal flow of code (without have to use GOTO because apparently they are evil). And to ensure clean-up after handling the error, the error handling section should start with "On Error Resume Next" and end with "Resume Exit_Command212_Click".
The REAL problem with this pattern is that organizations are forcing developers to use it for every single function. I have seen many functions that abuse the clean-up block for cleaning local variables (e.g Set myPerson = Nothing) because they didn't understand that local variables go out of scope and that setting the reference to Nothing doesn't change a single thing.
I would rather compare this pattern to the "using" block in C# because you should only use it when you have to free resources, like database connections and the like
-
@bjolling said:
I would rather compare this pattern to the "using" block in C# because you should only use it when you have to free resources, like database connections and the like
That's the only time you can use it. The using statement requires the target to implement IDisposable, which means that there are unmanaged resources to free. Unless the author of the component implemented IDisposable for no reason, but that's an edge case I wouldn't really concern myself with.
So, no, not really such a great comparison.
-
@Aaron said:
Not sure what you are disagreeing with. This is what I said:@bjolling said:
I would rather compare this pattern to the "using" block in C# because you should only use it when you have to free resources, like database connections and the like
That's the only time you can use it. The using statement requires the target to implement IDisposable, which means that there are unmanaged resources to free. Unless the author of the component implemented IDisposable for no reason, but that's an edge case I wouldn't really concern myself with.
So, no, not really such a great comparison.
- In C# you use the "using" block to free unmanaged resources (indeed like you say, on objects implementing the IDisposable interface, e.g. database connections)
- in VB6, you should use the OP's pattern only if you need to free resources like database connections, not for the joy of dereferencing local objects
In my quote above, I have put "it" in bold. This "it" was referring to the OP's VB6 pattern (not to the using block). It's not the perfect comparison if you only look at the flow control which resembles more try-catch-finally like already mentioned. But I still believe that the usage of both patterns is very similar.