2

How would I do an inline error handling routine in VBA? I don't want to put the error handler at the end.

This is from CPearson's Error Handling in VBA

Sub testErrHandling()
    On Error GoTo ErrHandler:

    Debug.print 9 / 0 'divide by zero error

    Worksheets("NewSheet").Activate 'missing worksheet error

    'more code here

    Exit Sub

ErrHandler:
    If Err.Number = 9 Then
        ' sheet does not exist, so create it
        Worksheets.Add.Name = "NewSheet"
        ' go back to the line of code that caused the problem
        Resume
    End If
End Sub

But I'm looking for something more like a Try/Catch block in VB.net

Community
  • 1
  • 1
D_Bester
  • 5,723
  • 5
  • 35
  • 77
  • VBA is much older than VB.NET and there isn't an equivalent way to do a try/catch block. You could *fake* it with a quite weird syntax and multiple `goto`s but I see no point in doing that really.. –  Jan 07 '15 at 08:23
  • @vba4all You should check out my answer below. I'm quite proud of my innovation. You can call it weird if you want but history often has progress overtaking the naysayers. It certainly isn't bad code IMHO. – D_Bester Jan 07 '15 at 13:15
  • I see exactly what you did there but to me the `Resume EndTry1` ain't any different then saying `GoTo ` and random `goto`s are bad habits and should be avoided at all costs. Btw what you've done here [been generally known for years already](http://www.vbforums.com/showthread.php?448403-Classic-VB-Why-do-errors-crash-my-program-and-how-can-I-stop-that-from-happening) *just hasn't probably been mentioned on SO*. –  Jan 07 '15 at 14:09
  • 1
    And also see this: [Inline Error Handling](http://msdn.microsoft.com/en-gb/library/aa242093%28v=vs.60%29.aspx) –  Jan 07 '15 at 14:17
  • You can simulate a TRY CTACH block in VBA Please see here http://stackoverflow.com/q/30991653/4413676 – HarveyFrench Jun 22 '15 at 23:45
  • Good discussion on this (I see you OP have posted an answer here, so it's for the benefit of everyone else): [Try catch statement in VBA using the standard VBA error handling statements](https://codereview.stackexchange.com/q/94415/146810) – Greedo Jun 20 '22 at 16:08

3 Answers3

3

This code will handle the error inline. This is a very cleanly structured pattern for handling an error. The flow moves very cleanly from top to bottom; no spaghetti code here.

VBA is an old language and has limitations. One of the ways to use error handling is to use Goto statements in the form of On Error Goto <Label> and Resume <Label>. This creates an opportunity.

Traditionally the error handler is placed at the bottom. But with the advances made in VB.net, it seems reasonable to leverage ideas to improve code. Try/Catch is a very structured way of handling errors and is very easy to follow. This pattern attempts to reproduce that in a very clean concise way. The flow is very consistent and doesn't jump from place to place.

Sub InLineErrorHandling()

    'code without error handling

BeginTry1:

    'activate inline error handler
    On Error GoTo ErrHandler1

    'code block that may result in an error
    Dim a As String: a = "Abc"
    Dim c As Integer: c = a 'type mismatch

ErrHandler1:

    'handle the error
    If Err.Number <> 0 Then

        'the error handler is now active
        Debug.Print (Err.Description)

    End If

    'disable previous error handler (VERY IMPORTANT)
    On Error GoTo 0
    'exit the error handler
    Resume EndTry1

EndTry1:

    'more code with or without error handling

End Sub

Sources:

Properly managed this works quite nicely. It is a very clean flowing pattern that is reproducible anywhere it is needed.

Community
  • 1
  • 1
D_Bester
  • 5,723
  • 5
  • 35
  • 77
  • try that in a real world environment - *if you know what i mean* ;) –  Jan 07 '15 at 16:19
  • I love this solution! is `Resume EndTry1` `EndTry1:` necessary? I found that it works the same if I remove them – 6diegodiego9 Jan 21 '22 at 21:42
  • @6diegodiego9 You can't assign a different error handler without using Resume... – D_Bester Jan 22 '22 at 01:01
  • Thanks D_Bester! You're right! After deeper study, I still found a couple or errors/inefficiences: the `Resume ...` must be moved at the end of the `If Err.Number <> 0` branch (and can be substituted by `On Error GoTo -1` reducing two lines to one line), for efficiency and because it results in a crash if left in your original position if there's not an active error to handle (ie if the "code block that may result in an error" doesn't result in an error). Also, the comment "activate inline error handler" should be "enable inline error handler" to use the correct MS terminology :) – 6diegodiego9 Jan 24 '22 at 16:50
  • I posted [here](https://stackoverflow.com/a/70837295/11738627) my revised solution – 6diegodiego9 Jan 24 '22 at 16:52
2

You can try assigning your object in a variable and use On Error Resume Next instead.

Dim sh As Worksheet

'This is essentially the "Try" part
On Error Resume Next 'this ignores the error
Set sh = Worksheets("NewSheet")
On Error Goto 0 'this resets the active error handling routine

'Then this is the "Catch" part I guess
If sh Is Nothing Then 'check is something is assigned to sh
    'And I think this is "Finally" part
    Set sh = Worksheets.Add: sh.Name = "NewSheet" 'add otherwise
End If

Not really familiar with the Try/Catch since I've not done some VB.Net but this is the closest inline error correction I can think of for your example. HTH.

L42
  • 19,427
  • 11
  • 44
  • 68
  • This is a great example of how to solve this specific problem. But the broader question was how do do error handling inline. – D_Bester Jan 07 '15 at 13:17
  • @D_Bester That is what I've provided exactly. OERN + OEG0 actually gives you the `Try` part. After that, you can actually have a `Select Case` or `If` (which will serve as your `Catch`) for the possible error numbers and react accordingly. I agree with vba4all regarding the use of `Goto` so as much as possible I avoid it as well. I limit myself to 1 `Goto` per code :) Well that's me. It might not be true to all. – L42 Jan 09 '15 at 00:57
  • Excellent code I agree! There are many problems that can be solved with this approach. I would call this a Try/Test method. And it does solve the error inline. – D_Bester Jan 09 '15 at 01:16
1

After months of insecurity/confusion about some obscure laws of VBA error handling, never fully satisfied by any of the popular documentation pages (Microsoft VBA/VB, C. Pearson and a others), I dedicated some days of trial and error to reconstruct the
complete set of the (written and unwritten/corrected) rules governing VBA error handling:

LEGEND:
1A: On Error Goto 0. 1B: On Error GoTo line/label. 1C: On Error Resume Next
2A: an error occurs. 2B: On Error GoTo -1
3: being in a subprocedure, temporarily, just until coming back
4: Erl
5: Resume, Resume Next, Resume line/label
6: Exit […], End […]

RULES:

  • With 1A in effect*, error handling is and stays disabled and inactive. This is the default.
  • With 1B in effect*, error handling is initially enabled and inactive; it's disabled while 3 and by 2A or 6, and it's activated on 2A and inactivated while 3 and by 5 (that also reenable it) or 6.
  • With 1C in effect*, error handling is initially enabled and inactive; it's disabled only while 3 and by 6 (it's not disabled by 2A!), and it always stays inactive (supposedly activated and inactivated immediately on 2A).
  • *= If 1A, 1B, 1C are called while error handling is active, the Err object is cleared immediately but the On Error action change effect is delayed until error handling is inactivated (by 5 or 6).
  • 2B (instantaneously) inactivates error handling (if any); error handling returns(1B)/stays(1C) enabled.
  • 1A, 1B, 1C, 2B, 5, 6 also instantaneously clears the Err object.
  • If an error occurs while in a subprocedure, if unhandled (disabled) in the current subprocedure it's passed to the first calling (parent) procedure where it's enabled and inactive. If no one is found, it stays there (in the subproc.).
  • 4 if error handling is active returns the line (if specified as a number label) of last error, otherwise 0.
  • 5 (correctly) crashes (err 21: "Resume without error") if called while error handling is inactive.

With all these rules fresh in mind, and starting from the D_Bester's solution posted here, here's my revised solution correcting a couple of errors/inefficiences in his code:

    Sub InLineErrorHandling()
    
        'code without error handling

        On Error GoTo ErrHandler1  'enable error handler
        'code block that may result in an error
        Dim a As String: a = "Abc"
        Dim c As Integer: c = a 'type mismatch

        'inline error handler routine
    ErrHandler1:
        If Err.Number <> 0 Then
            Debug.Print err.Description
            On Error GoTo -1 ' inactivate error handler
        End If        
        On Error GoTo 0 'disable error handler
        
        'more code without error handling  (default mode)
        Err.Raise 123
    
    End Sub

... where if you want to avoid using the undocumented On Error Goto -1 you can change the error handling routine this way:

    ErrHandler1:     If Err.Number <> 0 Then
                         Debug.Print err.Description
                         Resume ErrHandler1end 'inactivate & exit error handler
                     End If        
    ErrHandler1end:  On Error GoTo 0 'disable error handler

... and if no errors are expected in the error handling routine, can be further reduced to this (very standard) alternative:
    Sub InLineErrorHandling()
    
        'code without error handling

        On Error Resume Next  'enable error handler        
        'code block that may result in an error
        Dim a As String: a = "Abc"
        Dim c As Integer: c = a 'type mismatch
    
        'inline error handler routine
        If Err.Number <> 0 Then
            Debug.Print err.Description
        End If
        On Error GoTo 0 'disable error handler
        
        'more code without error handling  (default mode)
        err.Raise 123
    
    End Sub

It also worth mentioning that, in both the choices, if we also want to know what line (first for 1B, last for 1C) of the "code block that may result in an error" resulted in an error, we can use the Erl function, like this:

                 ...
            'code block that may result in an error
    10      Dim a As String: a = "Abc"
    20      Dim c As Integer: c = a 'type mismatch

            'inline error handler routine
            If Err.Number <> 0 Then
                Debug.Print "Error """ & err.Description & """ in line " & Err
                 ...
6diegodiego9
  • 503
  • 3
  • 14