vba嵌套错误处理

时间:2017-03-30 17:44:55

标签: vba error-handling

我正在尝试了解如何针对下面引用的情况设置错误处理。 Sub A对其自己的上下文进行了错误处理。然而,它调用Sub B创建数据库连接,结果集等,它在错误处理程序中清理。它的方式,似乎只有ErrorHandler A在整个过程中都是活跃的。如何在调用B时激活ErrorHandler B,并在B返回后恢复为错误处理。

由于

Sub A
' Preps for database access
On Error GoTo ErrorHandlerA

B
.
.

Exit Sub
ErrorHandlerA:
...
Resume
End Sub

Sub B
' Does database access stuff

On Error ErrorHandlerB

cleanUp:
con.close
rs.close

Exit Sub
ErrorHandlerB:
GoTo cleanup

End Sub

1 个答案:

答案 0 :(得分:4)

Exit Sub
ErrorHandlerA:
...
Resume
End Sub

Resume在这里,恢复正常执行(清除“错误处理模式”)并跳转到导致错误的指令。据推测(希望?)...包含使之前失败的调用成功的代码。

cleanUp:
con.close
rs.close

Exit Sub
ErrorHandlerB:
GoTo cleanup

End Sub

这里你是GoTo - 跳出错误处理子程序而不恢复正常执行,因此cleanUp行标签下的指令执行错误 - 处理上下文。那应该是Resume cleanUp,而不是GoTo

Exit Sub语句离开作用域,清除错误状态并隐式恢复正常执行。这是一个小MCVE

Public Sub Test()
On Error GoTo A
    DoSomething
    Debug.Print "After DoSomething: " & Err.Number
    Exit Sub
A:
    Debug.Print "Inside Test[A]: " & Err.Number
End Sub

Private Sub DoSomething()
On Error GoTo A

    Err.Raise 5

B:
    Debug.Print "Inside DoSomething[B]: " & Err.Number
    Exit Sub
A:
    Debug.Print "Inside DoSomething[A]: " & Err.Number
    Resume B
End Sub

运行Test过程会产生以下输出:

Inside DoSomething[A]: 5
Inside DoSomething[B]: 0
After DoSomething: 0

更改Resume B的{​​{1}}指令会产生此输出:

GoTo B

正如您所看到的,由于错误是在Inside DoSomething[A]: 5 Inside DoSomething[B]: 5 After DoSomething: 0 中处理的,因此在执行返回DoSomething时会重置错误状态,并且永远不会触发Test过程中的处理程序。 / p>

如果要将错误传播给调用者,则有许多选项:

  • 不处理被调用程序中的错误。在上面的示例中,这意味着Test不会有DoSomething语句;任何运行时错误都会“冒泡”调用堆栈,输出现在看起来像这样:

    On Error

    当调用代码知道最佳操作方法(例如向用户显示Inside Test[A]: 5 或记录错误等)时,通常会执行此操作。

  • 处理错误,然后重新提升。在您的示例中,这意味着将清理代码移动(或复制)到MsgBox子例程,并调用ErrorHandlerB而不是Err.Raise Err.Number。或者保留Resume,然后清理子例程可以执行GoTo以有效地重新抛出错误,以便调用者也可以处理。换句话说:

    If Err.Number <> 0 Then Err.Raise Err.Number

    输出:

    Public Sub Test()
    On Error GoTo A
        DoSomething
        Debug.Print "After DoSomething: " & Err.Number
        Exit Sub
    A:
        Debug.Print "Inside Test[A]: " & Err.Number
    End Sub
    
    Private Sub DoSomething()
    On Error GoTo A
    
        Err.Raise 5
    
    B:
        Debug.Print "Inside DoSomething[B]: " & Err.Number
        If Err.Number <> 0 Then Err.Raise Err.Number
        Exit Sub
    A:
        Debug.Print "Inside DoSomething[A]: " & Err.Number
        GoTo B 'resuming would clear the error and prevent rethrow
    End Sub
    

根据您希望在两个不同位置处理相同错误的确切原因,您还可以考虑使Inside DoSomething[A]: 5 Inside DoSomething[B]: 5 Inside Test[A]: 5 成为B,返回Function表示成功或失败 - 在这种情况下Boolean本身不再处理错误,而是使用正常的流量控制来确定要执行的操作:

A

产生此输出:

Public Sub Test()
On Error GoTo A
    If Not DoSomething Then
        Debug.Print "After DoSomething failed: " & Err.Number
        Exit Sub
    End If
    Exit Sub
A:
    Debug.Print "Inside Test[A]: " & Err.Number
End Sub

Private Function DoSomething() As Boolean
'Dim success As Boolean
On Error GoTo A

    Err.Raise 5
    'success = True

B:
    Debug.Print "Inside DoSomething[B]: " & Err.Number
    DoSomething = (Err.Number = 0) 'DoSomething = success
    Exit Function
A:
    Debug.Print "Inside DoSomething[A]: " & Err.Number
    'success = False
    GoTo B 'Resume B
End Function

注意Inside DoSomething[A]: 5 Inside DoSomething[B]: 5 After DoSomething failed: 0 知道Test失败,但DoSomething为0.这通常比使用运行时错误进行流量控制更可取,同样取决于您的实际情况。另请注意,在这种情况下,Err.Number跳转是可以避免的,使用一个简单的布尔局部变量来跟踪您的返回值(注释掉的代码)。