【问题标题】:On error exit sub and return error to sub that called erroneous sub出错时退出子程序并将错误返回给调用错误子程序的子程序
【发布时间】:2016-06-11 13:10:00
【问题描述】:

一些背景:

Precursor:我已经查看了其他错误处理问题,但我无法将答案完全应用于我的情况。我觉得Err.Raise 是我将如何完成我将在下面描述的内容。但是,我不确定如何以我需要的方式实现它。如果我要使用Err.Raise,我将如何在主子中引发错误代码之前先退出 Sub1-15?

话虽如此,

我有一个执行大量不同例程的大型 Excel VBA 项目。我选择从一个主例程中调用所有例程,以便以后对各个例程进行维护。我的主子程序中有一个 On Error 处理程序,如果从该主程序调用的任何程序中抛出错误,我希望触发它。

有没有办法:

  1. 记录
    • 发生的错误类型
    • 错误信息
    • 引发错误的子
  2. 在出错时退出该子以返回主子,然后
    • 引发刚刚在另一个子中发生的错误,以便调用 NotifyandRepair 错误处理程序?

我有以下情况

Sub MainSub()
    On Error GoTo NotifyandCorrect
    Call Sub1
    Call Sub2
    ...
    Call Sub15
    Exit Sub
NotifyandCorrect:
    'Send copy of faulty file, the error code and Sub that caused it
    'Then stop macro execution completely
End Sub

Sub Sub1()
    On Error Exit Sub1 and raise current Error in MainSub(?)
    'Perform data checks
End Sub

Sub Sub2()
    On Error Exit Sub2 and raise current Error in MainSub(?
    'Modify data groups
End Sub

Sub Sub15()
    On Error Exit Sub15 and raise current Error in MainSub(?
    'Clean up work
End Sub

无论如何我可以避免对 Sub1-Sub15 中的每一个都执行类似下面的操作吗?

Sub MainSub()
        On Error GoTo NotifyandCorrect
        Call Sub1
        Call Sub2
        ...
        Call Sub15
        Exit Sub
    NotifyandCorrect:
        'Send copy of faulty file, the error code and Sub that caused it
        'Then stop macro execution completely
    End Sub
    ...
    ...
Sub Sub15()
    On Error Goto HaltExecution
    'Clean up work
    Exit Sub
HaltExecution:
    'Note Error message & type
    'Note that Sub15 is where error occurred
    End Sub

结束问题

  1. 这可能吗?
  2. 如果这不可能,我应该如何处理它以执行我所描述的操作?您有什么建议(如果可以,请提供示例)

【问题讨论】:

    标签: vba excel error-handling error-logging


    【解决方案1】:

    您可以使用Err.NumberErr.Description 获取有关错误的信息。

    接下来,我建议创建一个临时 string 并在输入新子时更新它。如:

    Sub Sub1()
    temp= "sub1"
    ...
    End Sub
    
    Sub Sub2()
    temp= "sub2"
    ...
    End Sub
    

    因此,无论何时处理错误,字符串 temp 都会保存它发生的子项的值。

    【讨论】:

    • 但是,当错误发生时,我将如何返回到我的 MainSub 并引发 NotifyandRepair 错误处理程序?
    • 我认为您只需要删除主子上的“退出子”,然后每当子1或子2发生错误时,您立即返回主子中的NotifyandCorrect。
    【解决方案2】:

    您需要处理“子”方法中的错误,并让它们“重新抛出”错误(在错误处理程序子例程中使用 Err.Raise)以便调用者看到它 - 重新抛出时,指定方法的名称作为“源”。以下代码产生此输出:

    5            Invalid procedure call or argument        DoSomething1
    9            Subscript out of range      DoSomething2
    
    Public Sub MainSub()
        On Error GoTo ErrHandler
    
        DoSomething1
        DoSomething2
    
        Exit Sub
    ErrHandler:
        Debug.Print Err.Number, Err.Description, Err.Source
        Resume Next
    End Sub
    
    Private Sub DoSomething1()
        On Error GoTo ErrHandler
    
        Err.Raise 5
    
        Exit Sub
    ErrHandler:
        Err.Raise Err.Number, "DoSomething1", Err.Description
    End Sub
    
    Private Sub DoSomething2()
        On Error GoTo ErrHandler
    
        Err.Raise 9
    
        Exit Sub
    ErrHandler:
        Err.Raise Err.Number, "DoSomething2", Err.Description
    End Sub
    

    无论如何我可以避免为每个 Sub1-Sub15 做类似下面的事情吗?

    没有。每个过程都必须处理运行时错误,这是没有办法的。


    在硬编码字符串中指定方法名称很烦人。通过将每个过程封装到自己的对象中(例如,一些ICommand 实现),您可以利用TypeName 函数实现相同的结果:

    模块1

    Option Explicit
    
    Public Sub MainSub()
        On Error GoTo ErrHandler
    
        RunCommand New DoSomething1
        RunCommand New DoSomething2
    
        Exit Sub
    ErrHandler:
        Debug.Print Err.Number, Err.Description, Err.Source
        Resume Next
    End Sub
    
    Private Sub RunCommand(ByVal command As ICommand)
        command.Execute
    End Sub
    

    ICommand(类模块)

    Public Sub Execute()
    End Sub
    

    DoSomething1(类模块)

    Option Explicit
    Implements ICommand
    
    Private Sub ICommand_Execute()
        On Error GoTo ErrHandler
    
        Err.Raise 5
    
    ErrHandler:
        Err.Raise Err.Number, TypeName(Me), Err.Description
    End Sub
    

    DoSomething2(类模块)

    Option Explicit
    Implements ICommand
    
    Private Sub ICommand_Execute()
        On Error GoTo ErrHandler
    
        Err.Raise 9
    
    ErrHandler:
        Err.Raise Err.Number, TypeName(Me), Err.Description
    End Sub
    

    ICommand 接口并不是真正需要的,但它形式化了每个DoSomething 命令的调用方式。这个想法是有一个 object 来实现每个过程 - 这样您就可以将 TypeName(Me) 作为您的错误源,而无需对字符串进行硬编码。您将在 15 个专用类模块中拥有 15 个方法,而不是在单个标准模块中拥有 15 个过程。

    【讨论】:

    • 所以Err.Raise不仅会召回错误,还会导致代码移回原来的子/主程序触发NotifyandRepair
    • 是的。错误处理子例程中发生的任何错误都需要由调用者处理。请注意,调用Err.Raise 不带任何参数 将有效地“重新抛出”发生的确切错误,以便调用者处理 - 您只需要指定参数,因为您想指定 Source在这里。
    • 我不知道Err.Raise 能够像您描述的那样传递参数。非常适合我的情况,谢谢!不幸的是,不得不在每个 sub 中进行错误处理,但是我想这是一个很小的代价,对吧?
    • @CaffeinatedCoder 我添加了一种更 OOP 的方法,它不依赖于魔术字符串,如果有帮助的话。
    • 谢谢@Mat'sMug,我非常感谢您为向我解释这一点付出了多少努力。对于这个特定项目,将坚持使用您解释的第一个选项,因为我计划将我的加载项的每个部分(大宏)保留在单独的模块中。但是,我会在以后的项目中记住第二种方法,因为在你解释之前我并没有真正理解类模块的使用。每次与您交谈时,我都会学到一些新东西:)
    猜你喜欢
    • 2017-09-28
    • 1970-01-01
    • 2012-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多