VBA错误处理的好模式

jwo*_*ard 71 vba exception-handling exception

VBA中的错误处理有哪些好的模式?

特别是在这种情况下我应该怎么做:

... some code ...
... some code where an error might occur ...
... some code ...
... some other code where a different error might occur ...
... some other code ...
... some code that must always be run (like a finally block) ...
Run Code Online (Sandbox Code Playgroud)

我想处理这两个错误,并在可能发生错误的代码之后恢复执行.此外,最后的代码必须始终运行 - 无论先前抛出什么异常.我怎样才能达到这个目的?

gui*_*ooo 98

VBA中的错误处理


  • On Error Goto ErrorHandlerLabel
  • Resume(Next| ErrorHandlerLabel)
  • On Error Goto 0 (禁用当前错误处理程序)
  • Err 宾语

Err对象的属性通常被重置为零或错误处理程序零长度字符串,但它也可以用显式执行Err.Clear.

错误处理例程中的错误正在终止.

范围513-65535可用于用户错误.对于自定义类错误,您可以添加vbObjectError错误号.请参阅MS文档Err.Raise错误编号列表.

对于派生类中未实现的接口成员,应使用常量E_NOTIMPL = &H80004001.


Option Explicit

Sub HandleError()
  Dim a As Integer
  On Error GoTo errMyErrorHandler
    a = 7 / 0
  On Error GoTo 0

  Debug.Print "This line won't be executed."

DoCleanUp:
  a = 0
Exit Sub
errMyErrorHandler:
  MsgBox Err.Description, _
    vbExclamation + vbOKCancel, _
    "Error: " & CStr(Err.Number)
Resume DoCleanUp
End Sub

Sub RaiseAndHandleError()
  On Error GoTo errMyErrorHandler
    ' The range 513-65535 is available for user errors.
    ' For class errors, you add vbObjectError to the error number.
    Err.Raise vbObjectError + 513, "Module1::Test()", "My custom error."
  On Error GoTo 0

  Debug.Print "This line will be executed."

Exit Sub
errMyErrorHandler:
  MsgBox Err.Description, _
    vbExclamation + vbOKCancel, _
    "Error: " & CStr(Err.Number)
  Err.Clear
Resume Next
End Sub

Sub FailInErrorHandler()
  Dim a As Integer
  On Error GoTo errMyErrorHandler
    a = 7 / 0
  On Error GoTo 0

  Debug.Print "This line won't be executed."

DoCleanUp:
  a = 0
Exit Sub
errMyErrorHandler:
  a = 7 / 0 ' <== Terminating error!
  MsgBox Err.Description, _
    vbExclamation + vbOKCancel, _
    "Error: " & CStr(Err.Number)
Resume DoCleanUp
End Sub

Sub DontDoThis()

  ' Any error will go unnoticed!
  On Error Resume Next
  ' Some complex code that fails here.
End Sub

Sub DoThisIfYouMust()

  On Error Resume Next
  ' Some code that can fail but you don't care.
  On Error GoTo 0

  ' More code here
End Sub
Run Code Online (Sandbox Code Playgroud)

  • @PsychoData,这是一个错误代码列表http://support.microsoft.com/kb/146864 (3认同)

Joe*_*win 35

我还要补充一下:

  • 全局Err对象是您与异常对象最接近的对象
  • 你可以有效地"抛出异常" Err.Raise

而且只是为了好玩:

  • On Error Resume Next 是魔鬼的化身和被避免,因为它默默地隐藏错误

  • 不对.正确使用时On Error Resume Next相当于try/catch.正确使用只需要在_every line_之后检查或保存错误状态.它确实使复杂的错误检查更加冗长.但是,如果使用不当,以上所有内容都适用. (11认同)
  • 关于On Eror Resume Next的警告+1.可能是VB程序通常充满错误的首要原因之一. (10认同)
  • 我想每个人都会同意On Error相当于Try/Catch yes ...但On Error Resume Next?它会导致所有错误消失 - 包括我们从未预料到的错误.为什么有些奇怪的事情会发生错误,让错误在几个星期之后让错误消失[在调试其他人的代码时发生在我身上].我只在非常特殊的情况下使用它,紧凑的小函数,其中一个奇怪的东西迫使你犯错误(例如,这个项目存在于集合中). (3认同)
  • 如果在errMyErrorHandler中放入太多代码:您可能会在错误处理程序中发生错误,从而产生无限循环.如果在errMyErrorHandler中处理错误之前放置On Error Resume Next,则会重置Err对象并丢失错误信息.我将我的错误处理移动到sub并将err.num和description作为参数传递,这样我就可以使用On Error Resume Next,因为我重置了像screenupdating和cursor等所有内容并使用param值显示错误...`call mdl_val .usr_sub_handle_error(Err.Source,Err.Description)` (2认同)
  • “要避免”不完全是。有很多情况需要“On Error Resume Next”。这些情况的共同原则是通过抛出异常返回某些结果。最常见的情况是通过字符串键访问 `Collection` 对象:在这种情况下,调用者无法知道 `Collection` 对象中是否存在具有该键的项目。 (2认同)

Joh*_*lan 18

所以你可以这样做

Function Errorthingy(pParam)
On Error GoTo HandleErr

 ' your code here

    ExitHere:
    ' your finally code
    Exit Function

    HandleErr:
        Select Case Err.Number
        ' different error handling here'
        Case Else
            MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "ErrorThingy"
        End Select


   Resume ExitHere

End Function
Run Code Online (Sandbox Code Playgroud)

如果你想烘焙自定义异常.(例如违反业务规则的那些)使用上面的示例,但使用goto根据需要改变方法的流程.

  • 不知道这是不是惯用的VBA,但是对于.NET开发者来说,如果你将"HandleErr"重命名为"Catch",将"ExitHere"重命名为"Finally"并眯眼...... (4认同)
  • 这几乎就是我们过去处理大型 VB6 应用程序中的错误的方式。效果相对较好并且易于使用。IIRC,我们调用了一个错误处理类,而不是在函数中包含错误代码。这样,改变行为也变得容易得多。 (2认同)

Lim*_*awk 11

这是我的标准实现.我喜欢标签是自我描述的.

Public Sub DoSomething()

    On Error GoTo Catch ' Try
    ' normal code here

    Exit Sub
Catch:

    'error code: you can get the specific error by checking Err.Number

End Sub
Run Code Online (Sandbox Code Playgroud)

或者,用一个Finally块:

Public Sub DoSomething()

    On Error GoTo Catch ' Try

    ' normal code here

    GoTo Finally
Catch:

    'error code

Finally:

    'cleanup code

End Sub
Run Code Online (Sandbox Code Playgroud)

  • 如果在`Finally`块之后出现错误,它将抛出错误.它不会重新循环回到`Finally`块.(尝试一下,你会看到.)如果你想在Finally块之后处理一个错误,你需要添加另一个`On Error GoTo`,但可能还有另一个标签,比如`Catch2`.但是在这里我们开始深入研究清洁代码方法 - >一个干净的方法只需要一个错误处理程序(甚至应该有它自己的专用方法来捕获错误.) (2认同)

Dic*_*ika 6

Professional Excel Development有一个很好的错误处理方案。如果您打算在 VBA 中花费任何时间,那么这本书可能是值得的。VBA 在许多领域都缺乏,本书对管理这些领域提出了很好的建议。

PED 描述了两种错误处理方法。主要的是一个系统,其中所有入口点过程都是子过程,所有其他过程都是返回布尔值的函数。

入口点过程使用 On Error 语句来捕获几乎按照设计的错误。如果没有错误,非入口点过程返回 True,如果有错误,则返回 False。非入口点过程也使用 On Error。

两种类型的过程都使用中央错误处理过程来保持错误状态并记录错误。


小智 5

与讨论相关的还有相对未知的Erl功能。如果您的代码过程中有数字标签,例如,

Sub AAA()
On Error Goto ErrorHandler

1000:
' code
1100:
' more code
1200:
' even more code that causes an error
1300:
' yet more code
9999: ' end of main part of procedure
ErrorHandler:
If Err.Number <> 0 Then
   Debug.Print "Error: " + CStr(Err.Number), Err.Descrption, _
      "Last Successful Line: " + CStr(Erl)
End If
End Sub
Run Code Online (Sandbox Code Playgroud)

Erl函数返回最近遇到的数字行标签。在上面的示例中,如果运行时错误发生在 label 之后1200:但之前1300:,该Erl函数将返回1200,因为这是最近成功遇到的行 label。我发现在错误处理块的正上方放置一个行标签是一个很好的做法。我通常用来9999表示程序的主要部分已达到预期的结论。

笔记:

  • 行标签必须是正整数——像这样的标签MadeItHere:不能被 识别Erl

  • 行标签与 的实际行号完全无关VBIDE CodeModule。您可以按照您想要的顺序使用任何您想要的正数。在上面的示例中,只有 25 行左右的代码,但行标签编号从 开始1000。与 一起使用的编辑器行号和行标签号之间没有关系Erl

  • 行标签编号不需要按任何特定顺序,虽然如果它们不是按升序、自上而下的顺序,则功效和好处Erl会大大减弱,但Erl仍会报告正确的编号。

  • 线路标签特定于它们出现的过程。如果过程ProcA调用过程并且在将控制权传递回过程ProcB中发生错误,(in ) 将返回调用之前最近遇到的行标签号。从内部,您无法获取可能出现在 中的行标签号。ProcBProcAErlProcAProcAProcBProcAProcB

在循环中放置行号标签时要小心。例如,

For X = 1 To 100
500:
' some code that causes an error
600:
Next X
Run Code Online (Sandbox Code Playgroud)

如果行标签后面500但之前的代码600导致错误,并且该错误在循环的第 20 次迭代中出现,Erl则将返回500,即使600在循环的前 19 次迭代中已成功遇到该错误。

Erl在程序中正确放置行标签对于使用该功能获取真正有意义的信息至关重要。

互联网上有许多免费实用程序可以自动在过程中插入数字行标签,因此您在开发和调试时可以获得细粒度的错误信息,然后在代码上线后删除这些标签。

如果您的代码在发生意外错误时向最终用户显示错误信息,则提供Erl该信息中的值可以使查找和修复问题变得比不报告值时简单得多Erl