【问题标题】:Restoring Old VBA On Error出错时恢复旧 VBA
【发布时间】:2018-01-04 18:55:57
【问题描述】:

假设您在主子中定义了 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

注意在 sub XYZ 中输入的 cmets。 也就是说,在经历程序崩溃后控制转移的地方是基于最后的“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。

有没有办法做到这一点?

【问题讨论】:

    标签: vba onerror


    【解决方案1】:

    是的,在 XYZ() 中

    On Error Goto 0
    

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

    【讨论】:

    • 如此简单...我不知道它会弹回父子。谢谢。
    【解决方案2】:

    当然。没有直接的clearing local error handling,您可以有条件地重新抛出处理程序中的错误:

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

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

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

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

    上面的 sn-p 本质上就是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:您的过程可能负责太多事情,将其拆分。

    【讨论】:

    • FWIW,如果XYZ 中的On Error GoTo 0 强制在MainCatchAll 处理程序中处理错误,则会自动保留原始错误消息。
    • 你不能在错误退出子上返回主并进入错误处理吗? (也许是一个新的错误处理程序,但可能没有你想要的结果)没有错误然后继续进入 sub 并使用 subs 错误处理程序?您想返回 main 并出现错误,我想您是在问是否遇到一个错误处理程序,它是否会取代以前的错误处理程序。
    • @YowE3K 我不是故意暗示的。我的意思是Err.Raise Err.Number 有效地重新抛出了同样的错误,包括原始消息和来源。编辑澄清。
    • @Wookies-Will-Code 当然可以。我已经进行了编辑以澄清,希望对您有所帮助。
    • 处理一些错误并重新抛出其他错误的编辑消除了我对您想说的内容的误解。
    【解决方案3】:

    这很容易回到 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 子错误处理程序,那么使用 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

    【讨论】:

    • 我想你们已经拥有了它,来自许多人的 Goto 0,@S Meaden、*YowE3k 和 *Mat's Mug。如果你想要更多的控制和冒泡,那么还有其他方法,但他只是踢给调用者来处理它。 - WWC
    • 以上两个答案都是正确的,具体取决于您想要什么,我对它们都进行了测试。伙计们干得好!
    猜你喜欢
    • 2014-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多