良好的VBAerror handling模式

VBA中的error handling有什么好的模式?

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

... 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) ... 

我想要处理这两个错误,并在错误发生后的代码之后恢复执行。 另外,最后的代码必须总是运行 – 不pipe之前抛出什么exception。 我怎样才能达到这个结果?

在VBA中的error handling

  • On Error Goto ErrorHandlerLabel
  • ResumeNext | ErrorHandlerLabel
  • On Error Goto 0 (禁用当前的error handling程序)
  • Err对象

Err对象的属性通常在error handling例程中重置为零或零长度string,但也可以使用Err.Clear显式地完成。

error handling例程中的错误正在终止。

范围513-65535可用于用户错误。 对于自定义类错误,您将vbObjectError添加到错误号。

对于派生类中未实现的接口成员,应该使用常量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 

我还要补充:

  • 全局的Err对象是最接近你的exception对象的
  • Err.Raise可以有效地“抛出exception”

而只是为了好玩:

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

所以你可以做这样的事情

 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 

如果你想烤自定义的例外。 (例如违反业务规则的)使用上面的例子,但是使用goto来根据需要改变方法的stream程。

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

 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 

或者,用一个Finally块:

 Public Sub DoSomething() On Error GoTo Catch ' Try ' normal code here GoTo Finally Catch: 'error code Finally: 'cleanup code End Sub 

专业的Excel开发有一个相当不错的error handlingscheme 。 如果你打算花时间在VBA上,这本书可能是值得的。 VBA缺乏的地方有很多,本书对这些领域的pipe理提出了很好的build议。

PED描述了两种error handling方法。 主要的是一个系统,所有的入口点程序都是子程序,所有其他的程序都是返回布尔值的函数。

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

这两种types的程序都使用中央error handling程序来保持错误状态并logging错误。

这是一个相当不错的模式。

对于debugging:当出现错误时,按下Ctrl-Break(或Ctrl-Pause),将断点标记(或任何被调用的)向下拖到Resume行,按F8键,你会跳到“扔”错误。

ExitHandler是你的“最后”。

沙漏每次都会被杀死。 状态栏文本将被清除每次。

 Public Sub ErrorHandlerExample() Dim dbs As DAO.Database Dim rst As DAO.Recordset On Error GoTo ErrHandler Dim varRetVal As Variant Set dbs = CurrentDb Set rst = dbs.OpenRecordset("SomeTable", dbOpenDynaset, dbSeeChanges + dbFailOnError) Call DoCmd.Hourglass(True) 'Do something with the RecordSet and close it. Call DoCmd.Hourglass(False) ExitHandler: Set rst = Nothing Set dbs = Nothing Exit Sub ErrHandler: Call DoCmd.Hourglass(False) Call DoCmd.SetWarnings(True) varRetVal = SysCmd(acSysCmdClearStatus) Dim errX As DAO.Error If Errors.Count > 1 Then For Each errX In DAO.Errors MsgBox "ODBC Error " & errX.Number & vbCrLf & errX.Description Next errX Else MsgBox "VBA Error " & Err.Number & ": " & vbCrLf & Err.Description & vbCrLf & "In: Form_MainForm", vbCritical End If Resume ExitHandler Resume End Sub Select Case Err.Number Case 3326 'This Recordset is not updateable 'Do something about it. Or not... Case Else MsgBox "VBA Error " & Err.Number & ": " & vbCrLf & Err.Description & vbCrLf & "In: Form_MainForm", vbCritical End Select 

它也陷阱DAO和VBA错误。 如果要捕获特定的Err编号,则可以在“VBA错误”部分放置一个Select Case。

 Select Case Err.Number Case 3326 'This Recordset is not updateable 'Do something about it. Or not... Case Else MsgBox "VBA Error " & Err.Number & ": " & vbCrLf & Err.Description & vbCrLf & "In: Form_MainForm", vbCritical End Select 

我使用了自己开发的一段代码,这对我的代码非常有用:

在函数或子的开始,我定义:

 On error Goto ErrorCatcher: 

然后,我处理可能的错误

 ErrorCatcher: Select Case Err.Number Case 0 'exit the code when no error was raised On Error GoTo 0 Exit Function Case 1 'Error on definition of object 'do stuff Case... 'little description here 'do stuff Case Else Debug.Print "###ERROR" Debug.Print " • Number :", Err.Number Debug.Print " • Descrip :", Err.Description Debug.Print " • Source :", Err.Source Debug.Print " • HelpCont:", Err.HelpContext Debug.Print " • LastDLL :", Err.LastDllError Stop Err.Clear Resume End Select 

我个人对此线程早期发表的观点:

而只是为了好玩:

在错误恢复接下来是恶魔化身和被避免,因为它默默地隐藏错误。

我在程序中使用了On Error Resume Next ,我不想让错误停止我的工作,而且任何语句都不依赖于前面的语句的结果。

当我这样做时,我添加一个全局variablesdebugModeOn ,并将其设置为True 。 然后我用这个方法:

 If not debugModeOn Then On Error Resume Next 

当我提交我的工作时,我将该variables设置为false,从而将错误隐藏到用户身上,并在testing过程中显示出来。

在做一些可能失败的事情时也使用它,比如调用可能为空的ListObject的DataBodyRange:

 On Error Resume Next Sheet1.ListObjects(1).DataBodyRange.Delete On Error Goto 0 

代替:

 If Sheet1.ListObjects(1).ListRows.Count > 0 Then Sheet1.ListObjects(1).DataBodyRange.Delete End If 

或者检查集合中是否存在某个项目:

 On Error Resume Next Err.Clear Set auxiliarVar = collection(key) ' Check existence (if you try to retrieve a nonexistant key you get error number 5) exists = (Err.Number <> 5) 

当心大象陷阱:

在这个讨论中我没有看到这一点。 [Access 2010]

ACCESS / VBA如何处理CLASS对象中的错误由一个可configuration的选项决定:

VBA代码编辑器>工具>选项>常规>错误陷印:

在这里输入图像描述

下面的代码显示了一个替代scheme,确保子/函数只有一个退出点。

 sub something() on error goto errHandler ' start of code .... .... 'end of code ' 1. not needed but signals to any other developer that looks at this ' code that you are skipping over the error handler... ' see point 1... err.clear errHandler: if err.number <> 0 then ' error handling code end if end sub