在错误语句上
即使有保护条款,也无法实际上总是考虑到程序正文中可能出现的所有可能的错误情况。On Error GoTo
语句指示 VBA 跳转到*行标签,*并在运行时发生意外错误时进入错误处理模式。处理完错误后,代码可以使用 Resume
关键字恢复 正常执行。
行标签表示子程序 :因为子程序源自传统的 BASIC 代码,并使用 GoTo
和 GoSub
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
行号与导致错误的语句不太相关,编号行很快变得乏味且不太适合维护。