【问题标题】:How to stop a VBA macro without destroying objects如何在不破坏对象的情况下停止 VBA 宏
【发布时间】:2013-08-02 11:37:06
【问题描述】:

我编写了一个 VBA 程序,它在Workbook_Open 上创建一个将错误写入错误日志的文件流。如果我遇到致命错误(因此需要停止执行宏),程序会执行End 语句,突然停止宏。我知道这个解决方案不是一个理想的解决方案,但是如果我有几个函数很深(即一个函数调用了另一个函数调用了另一个函数,并且第三个函数产生错误)。退出发生错误的函数只会影响该特定函数,导致可能出现无效数据、意外单元格值等。

但是这种方法给我带来了另一个问题——当End 执行时,它会破坏我的所有对象,包括错误流。因此,当用户执行新操作并遇到致命错误时,他们会收到 VBA 运行时错误(91: Object ... not set),因为代码写入现在设置为Nothing.的文件流

有没有更好的方法来结束宏(从而避免出现错误后的意外行为)而不丢失我的所有对象?官方的 VBA 文档没有帮助。提前致谢。

【问题讨论】:

  • 其他选项可能包括让每个函数在函数返回或全局变量中设置错误条件,如果函数 A 调用函数 B 出错,则通过函数 A 将结果返回给调用代码。或者可能让每个函数中的错误处理程序调用一个通用的正常关闭例程;当然,这是否实用取决于您的代码在做什么。
  • 级联函数应该使用返回值(或者如果需要某些变量通过ByRef)来通知更高级别的函数失败。在 VB6/VBA 中,您永远不需要 END
  • 除了其他人的建议之外:让您的日志记录全部通过一个过程可能更健壮,并让该过程负责根据需要打开/关闭流。保持流持续打开并不会节省太多时间。

标签: excel vba exception-handling error-handling garbage-collection


【解决方案1】:

在模块级别声明变量,而不是在函数中。在 VBA 中,默认情况下,您会在左侧的项目导航中看到各种工作表。工作表下方是一个名为“Modules”的文件夹:如果您没有看到“Module1”或作为该文件夹子级的变体,请右键单击该文件夹并选择“Insert\Module”。
这些对你来说应该是持久的。

【讨论】:

  • 不幸的是,我已经尝试过了。该对象处于模块级别。
  • 您是否尝试过在代码中使用断点而不是 END 命令?当光标在您希望它停止的代码行上时按 F9。这似乎是初级的,但是对象应该在那里保留它们的值,并且您应该能够使用手表/即时窗口来查看值是什么。 F8 将逐步执行代码(查看 Debug 菜单上的所有选项)
【解决方案2】:

也许试试Exit 而不是End

如果你试图退出一个函数

Function a()

If blahblah.. Then
    Exit Function
End If

End Function

我不确定您的对象是如何在哪些模块上声明和处理的,所以...如果您可以发布代码,它可能会有很大帮助。

【讨论】:

    【解决方案3】:

    结束:

    立即终止执行。从来不需要本身,但可能是 放置在过程中的任何位置以结束代码执行,关闭文件 使用 Open 语句打开并清除变量。执行时, End 语句重置所有模块级变量和所有静态变量 所有模块中的局部变量。

    ThisWorkbook 模块:

    Public fileSystem As FileSystemObject
    Public errorStream As TextStream
    
    Private Sub Workbook_Open()
    
        Set fileSystem = New FileSystemObject
        Set errorStream = fileSystem.CreateTextFile("c:\temp\error.log", True)
    
    End Sub
    

    标准模块:

    Public Sub First()
        If (Not ThisWorkbook.errorStream Is Nothing) Then
            Debug.Print VBA.TypeName(ThisWorkbook.errorStream)
        End If
    
        End
        ' Exit Sub
    End Sub
    
    Public Sub Second()
        If (Not ThisWorkbook.errorStream Is Nothing) Then
            Debug.Print VBA.TypeName(ThisWorkbook.errorStream)
        End If
    End Sub
    

    当'First' 方法首先执行'End' 然后'Second' 方法时,errorStream 将是Nothing。代替 'End' 使用 'ExitSub',则变量不会被重置。

    或者您可以在 Thisworkbook 类模块中将错误流变量设为私有并添加属性,如果变量为 Nothing,它将创建流。高温

    ThisWorkbook 模块:

    Private m_errorStream As TextStream
    Private Const FILE_PATH_NAME As String = "c:\temp\error.log"
    
    Public Property Get ErrorStream() As TextStream
        If (m_errorStream Is Nothing) Then
            Dim fileSystem As FileSystemObject
            Set fileSystem = New FileSystemObject
            If (fileSystem.FileExists(FILE_PATH_NAME)) Then
                Set m_errorStream = fileSystem.GetFile(FILE_PATH_NAME).OpenAsTextStream
            Else
                Set m_errorStream = fileSystem.CreateTextFile(FILE_PATH_NAME, False)
            End If
        End If
    
        Set ErrorStream = m_errorStream
    End Property
    

    标准模块:

    Public Sub First()
        If (Not ThisWorkbook.ErrorStream Is Nothing) Then
            Debug.Print VBA.TypeName(ThisWorkbook.ErrorStream)
        End If
    
        End
    End Sub
    
    Public Sub Second()
        If (Not ThisWorkbook.ErrorStream Is Nothing) Then
            Debug.Print VBA.TypeName(ThisWorkbook.ErrorStream)
        End If
    End Sub
    

    【讨论】:

      【解决方案4】:

      使用顶级错误处理程序,并且只捕获您想要处理的例程中的错误而不完全中止

      演示:

      Option Explicit
      
      Sub test()
      On Error GoTo Top_Error_Handler
      Debug.Print "Error handled in sub routine: test1"
      test1
      Debug.Print "Error NOT handled in sub routine: test2"
      test2
      Exit Sub
      
      Top_Error_Handler:
      MsgBox "Top Level Error Handler: Error Number:" & Err.Number _
           & ":" & Err.Description
      
      End Sub
      
      Sub test1()
      On Error Resume Next
      Debug.Print 1 / 0
      
      End Sub
      
      Sub test2()
      Debug.Print 1 / 0
      End Sub
      

      如您所见,test1 中的错误处理覆盖了主程序中的处理,因此不会引发错误。在第二个 Sub,test2 中,没有错误处理,因此信息被传递给前一个程序来处理(如果该程序被其他东西调用,它将向上传递它),并且错误可以由您的主程序干净地处理以整齐地关闭所有内容。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多