恢复旧 VBA 错误

Restoring Old VBA On Error

假设您在主子中定义了 On Error。

Sub Main()

   On Error Goto CatchAll
   '... Some code... goes here
   Call XYZ



CatchAll:
   Msgbox "An Unexpected Error Occurred"
   End
End Sub

在 Main sub 中,您调用了例程 XYZ。 假设 Sub XYZ 是这样的:

Sub XYZ()
   'If unexpected error happens here, control will be shifted to sub Main, Label CatchAll

   On Error Goto Errorz

   'If unexpected error happens here, control will be shifted to same sub, Label Errorz...

Errorz:
        Msgbox "You have crashed inside XYZ"
        End

End Sub

注意子 XYZ 中输入的评论。 也就是说,在程序崩溃后控制权的转移是基于最后的 "On Error Goto" 语句。

有没有办法在 VBA 中恢复旧的 On Error Goto?

换句话说,在 Sub XYZ 中,我有一些代码:

Sub XYZ()
   On Error Goto Errorz:

   'Some Code

   On Error Goto <Old Error Trapping Method>
   'Here I desire to go back to Main CatchAll: label.  Is there a way to do that?

End Sub

注意上面代码中的最后一条注释。我希望能够重新定义 On Error 以在我定义新行为之前恢复 On error 的最后一个行为(上一个行为:Goto Main.CatchAll 标签,新行为,Goto XYZ.Errorz 标签)。在代码的这一点上,我希望能够在错误时转到 Main.CatchAll。

有什么方法可以做到这一点吗?

是的,在 XYZ()

On Error Goto 0

应该清除当前过程的(即 XYZ() 的)错误处理程序,并在您的示例中将错误处理的控制权传递给 Main() 中的错误处理程序。

当然可以。除了直接 ,您可以有条件地 重新抛出 处理程序中的错误:

Errorz:
    With Err
        If .Number = SomeSpecificLocalError Then
            ' mitigate error
            Resume Next
        Else
            ' rethrow
            .Raise .Number
        End If
    End With

这样可以保留原始错误消息(您可以选择指定 source 和更具描述性的 message 供 "catch-all" 处理程序查看)和 "bubble"调用堆栈出错。

让我们用另一种语言来说明区别:

try
{
    // do stuff
}
catch
{
    // handle error
}

// do more stuff

上面的代码片段本质上是 On Error GoTo 0 所做的:无论 do more stuff 中发生什么,如果抛出错误,调用者都需要处理它。

现在比较:

try
{
    // do stuff
    // do more stuff
}
catch(InvalidOperationException e)
{
    // handle invalid operation
}
catch(FileNotFoundException)
{
    throw;
}
catch(Exception)
{
    // handle any other exception
}

这里本地范围将处理一个 InvalidOperationException,但是一个 FileNotFoundException 将显式 重新抛出 以供调用代码处理,而任何其他异常将在本地处理。

这两种方法各有利弊,我只是把它留在这里供后代使用:如果你想 可以 在 VBA 中重新抛出运行时错误.

也就是说,在单个过程中需要多个错误处理策略是一种代码味道 IMO:您的过程可能负责太多事情,将其拆分。

这很容易踢回 main 并进入您的错误处理程序。

Option Explicit
Private Sub CommandButton1_Click()

On Error GoTo CatchAll

Call XYZ

Exit Sub

CatchAll:
MsgBox "This is Catchall Error Handling"
End Sub

Private Sub XYZ()

'This will purposely throw an error
'ThisWorkbook.Worksheets("Sheet55").Range("A1").Value = 10

On Error GoTo 0
'This will purposely throw an error
ThisWorkbook.Worksheets("Sheet66").Range("A1").Value = 10

MsgBox "You made it to here? Wow!"

End Sub

如果你必须有一个 in sub 错误处理程序,下面的答案在使用 rethrow 时是正确的,也有效,伙计们,你们很聪明:

Option Explicit
Private Sub CommandButton1_Click()

On Error GoTo CatchAll

Call XYZ

Exit Sub

CatchAll:
MsgBox "This is Catchall Error Handling"
End Sub

Private Sub XYZ()

'This will purposely throw an error
'ThisWorkbook.Worksheets("Sheet55").Range("A1").Value = 10

On Error GoTo Err_XYZ
ThisWorkbook.Worksheets("Sheet66").Range("A1").Value = 10

MsgBox "You made it to here? Wow!"

Exit Sub

Err_XYZ:
With Err
.Raise .Number
End With
End Sub

干杯,-WWC