在错误语句上

即使有保护条款,也无法实际上总是考虑到程序正文中可能出现的所有可能的错误情况。On Error GoTo 语句指示 VBA 跳转到*行标签,*并在运行时发生意外错误时进入错误处理模式。处理完错误后,代码可以使用 Resume 关键字恢复 正常执行。

行标签表示子程序 :因为子程序源自传统的 BASIC 代码,并使用 GoToGoSub jumps 和 Return 语句跳回例程,如果事情没有严格的结构,编写难以理解的意大利面条代码相当容易。出于这个原因,最好是:

  • 一个过程只有一个错误处理子程序
  • 错误处理子例程只在错误状态下运行

这意味着处理其错误的过程应该像这样构造:

Private Sub DoSomething()
    On Error GoTo CleanFail

    'procedure code here

CleanExit:
    'cleanup code here
    Exit Sub

CleanFail:
    'error-handling code here
    Resume CleanExit
End Sub

错误处理策略

有时你希望使用不同的操作处理不同的错误。在这种情况下,你将检查全局 Err 对象,该对象将包含有关所引发错误的信息 - 并相应地执行操作:

CleanExit:
    Exit Sub

CleanFail:
    Select Case Err.Number
        Case 9
            MsgBox "Specified number doesn't exist. Please try again.", vbExclamation
            Resume
        Case 91
            'woah there, this shouldn't be happening.
            Stop 'execution will break here
            Resume 'hit F8 to jump to the line that raised the error
        Case Else
            MsgBox "An unexpected error has occurred:" & vbNewLine & Err.Description, vbCritical
            Resume CleanExit
    End Select
End Sub

作为一般准则,请考虑打开整个子例程或函数的错误处理,并处理其范围内可能发生的所有错误。如果你只需要处理代码的小部分中的错误 - 打开和关闭相同级别的错误处理:

Private Sub DoSomething(CheckValue as Long)

    If CheckValue = 0 Then
        On Error GoTo ErrorHandler   ' turn error handling on
        ' code that may result in error
        On Error GoTo 0              ' turn error handling off - same level
    End If

CleanExit:
    Exit Sub

ErrorHandler:
    ' error handling code here
    ' do not turn off error handling here
    Resume

End Sub

行号

VBA 支持传统风格(例如 QBASIC)行号。Erl hidden 属性可用于标识引发上一个错误的行号。如果你没有使用行号,Erl 将只返回 0。

Sub DoSomething()
10 On Error GoTo 50
20 Debug.Print 42 / 0
30 Exit Sub
40
50 Debug.Print "Error raised on line " & Erl ' returns 20
End Sub

如果你正在使用的行号,但不能始终如一,那么 Erl 将返回引发错误的指令之前的最后一个行号

Sub DoSomething()
10 On Error GoTo 50
   Debug.Print 42 / 0
30 Exit Sub

50 Debug.Print "Error raised on line " & Erl 'returns 10
End Sub

请记住,Erl 也只有 Integer 精度,并将无声地溢出。这意味着整数范围之外的行号将给出不正确的结果:

Sub DoSomething()
99997 On Error GoTo 99999
99998 Debug.Print 42 / 0
99999
      Debug.Print Erl   'Prints 34462
End Sub

行号与导致错误的语句不太相关,编号行很快变得乏味且不太适合维护。