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.


  • :belt_onion:

    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 Sub

     

    Because 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.Description

    Cleanup:

    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.


  • :belt_onion:

    @KattMan said:

    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.Description

    Cleanup:

    End Sub

    This then works exactly like Try-Catch-Finally

    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.

    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.


  • :belt_onion:

    @Aaron 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.

    Not sure what you are disagreeing with. This is what I said:

    • 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.


Log in to reply