【问题标题】:Properly Handling Errors in VBA (Excel)正确处理 VBA (Excel) 中的错误
【发布时间】:2011-08-27 01:31:17
【问题描述】:

我使用 VBA 已经有一段时间了,但我仍然不太确定错误处理。

一篇好的文章是 CPearson.com

但是我仍然想知道我以前执行 ErrorHandling 的方式是否完全错误: 第 1 块

On Error Goto ErrCatcher
   If UBound(.sortedDates) > 0 Then

       // Code

   Else
ErrCatcher:
       // Code

   End If

if 子句,因为如果它为真,将被执行,如果它失败,Goto 将进入 Else 部分,因为数组的 Ubound 不应该是零或更小,没有错误,这个方法有效到目前为止还不错。

如果我理解正确的话应该是这样的:

第 2 块

On Error Goto ErrCatcher
    If Ubound(.sortedDates) > 0 Then

       // Code
    End If

    Goto hereX

ErrCatcher:
       //Code
    Resume / Resume Next / Resume hereX

hereX:

甚至像这样: 第 3 块

On Error Goto ErrCatcher
    If Ubound(.sortedDates) > 0 Then

       // Code
    End If

ErrCatcher:
    If Err.Number <> 0 then
       //Code
    End If

我看到的最常见的方式是,错误“Catcher”位于 sub 的末尾,而 Sub 实际上以“Exit Sub”结束,但是如果反之跳读代码,Sub 是不是很大?

第 4 块

以下代码的来源: CPearson.com

  On Error Goto ErrHandler:
   N = 1 / 0    ' cause an error
   '
   ' more code
   '
  Exit Sub

  ErrHandler:

   ' error handling code'

   Resume Next

End Sub 

应该像第 3 区那样吗?

【问题讨论】:

  • 而不是冒险使用If Ubound(.sortedDates)&gt;0 使用If IsArrayAllocated(.sortedDates) = TRUE 引发错误
  • 哇!这很快:-) - 谢谢,这使得 On Error Goto 在这里没有必要......
  • 但如果不是数组检查..虽然我不能其他任何情况..我认为我的问题是这样回答的 - 没有办法投票赞成你的评论吗? ,因为它真的很好:-)

标签: excel vba


【解决方案1】:

您从 ray023 那里得到了一个非常了不起的答案,但您认为它可能有点矫枉过正的评论是恰当的。对于“更轻”的版本....

Block 1 是,恕我直言,不好的做法。正如 osknows 已经指出的那样,将错误处理与正常路径代码混合是不好的。一方面,如果一个 new 错误被抛出,同时存在一个有效的错误条件,你将不会有机会处理它(除非你从一个例程调用还有一个错误处理程序,执行将“冒泡”)。

块 2 看起来像是对 Try/Catch 块的模仿。应该没问题,但这不是 VBA 方式。 Block 3 是 Block 2 的变体。

Block 4 是 The VBA Way 的基本版本。我会强烈建议使用它或类似的东西,因为这是任何其他继承代码的 VBA 程序员所期望的。不过,让我介绍一个小扩展:

Private Sub DoSomething()
On Error GoTo ErrHandler

'Dim as required

'functional code that might throw errors

ExitSub:
    'any always-execute (cleanup?) code goes here -- analagous to a Finally block.
    'don't forget to do this -- you don't want to fall into error handling when there's no error
    Exit Sub

ErrHandler:
    'can Select Case on Err.Number if there are any you want to handle specially

    'display to user
    MsgBox "Something's wrong: " & vbCrLf & Err.Description

    'or use a central DisplayErr routine, written Public in a Module
    DisplayErr Err.Number, Err.Description

    Resume ExitSub
    Resume
End Sub

注意第二个Resume。这是我最近学到的一个技巧:它永远不会在正常处理中执行,因为Resume &lt;label&gt; 语句会将执行发送到其他地方。不过,它可能是调试的天赐之物。当您收到错误通知时,选择“调试”(或按 Ctl-Break,然后在收到“执行被中断”消息时选择“调试”)。下一个(突出显示的)语句将是 MsgBox 或以下语句。使用“设置下一条语句”(Ctl-F9) 突出显示裸露的Resume,然后按 F8。这将向您显示确切错误发生的位置。

关于您对这种“跳跃”格式的反对意见,A) 如前所述,这是 VBA 程序员所期望的,&B) 您的例程应该足够短,以便跳跃不远。

【讨论】:

  • 非常感谢。我想我仍然需要习惯 VBA 错误处理...感谢您的 Resume
  • 这里所有的好答案,但对于包括 ExitSub 的 +1:我发现始终退出 sub 总体上有助于我的错误处理和编码。我总是把我所有的清理代码放在那个块里。我通常也会将“On Error GoTo 0”作为该代码块的第一行,这样理论上,VBA 不会在我的清理代码中抛出错误,这通常是我想要的。
  • @Steve--实际上,On Error GoTo 0 所做的是关闭错误处理,因此如果抛出错误,您只会得到 VB(A) 的默认值带有错误编号和描述以及结束或调试选项的消息框。当我在做一些半风险的事情时(比如,关闭一个可能打开也可能不打开的数据库连接,我只关心它在完成后它是 not open),我把On Error Resume Next 在它前面。这使得 VB(A) 忽略错误。
  • @skofgar--我把这个技巧归功于 Wrox 的 Access 2007 Progammer's Reference。几乎值这本书本身的价格。
  • On Error GoTo 0 对我来说真的很有用,因为我遇到的问题是On Error Resume Next-block 不仅忽略了我想要的部分中的错误,而且还忽略了下面的整个代码,我不知道。但是感谢 StackOverflow 的所有人,我将能够编写更好的代码和更好的错误处理 :-)
【解决方案2】:

错误处理的两个主要目的:

  1. 可以捕获错误 预测但无法控制用户 从做(例如,将文件保存到 拇指驱动器时的拇指驱动器 已被删除)
  2. 对于意外错误,向用户提供一个表单 告诉他们问题出在哪里 是。这样,他们可以转发 给你留言,你也许可以 给他们一个解决方法,而你 解决问题。

那么,你会怎么做呢?

首先,创建一个错误表单,在发生意外错误时显示。

它可能看起来像这样(仅供参考:我的称为 frmErrors):

注意以下标签:

  • lbl 标题
  • lblSource
  • lbl问题
  • lbl响应

还有标准的命令按钮:

  • 忽略
  • 重试
  • 取消

这个表单的代码没有什么特别之处:

Option Explicit

Private Sub cmdCancel_Click()
  Me.Tag = CMD_CANCEL
  Me.Hide
End Sub

Private Sub cmdIgnore_Click()
  Me.Tag = CMD_IGNORE
  Me.Hide
End Sub

Private Sub cmdRetry_Click()
  Me.Tag = CMD_RETRY
  Me.Hide
End Sub

Private Sub UserForm_Initialize()
  Me.lblErrorTitle.Caption = "Custom Error Title Caption String"
End Sub

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
  'Prevent user from closing with the Close box in the title bar.
    If CloseMode <> 1 Then
      cmdCancel_Click
    End If
End Sub

基本上,您想知道表单关闭时用户按下了哪个按钮。

接下来,创建一个将在整个 VBA 应用程序中使用的错误处理程序模块:

'****************************************************************
'    MODULE: ErrorHandler
'
'   PURPOSE: A VBA Error Handling routine to handle
'             any unexpected errors
'
'     Date:    Name:           Description:
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'03/22/2010    Ray      Initial Creation
'****************************************************************
Option Explicit

Global Const CMD_RETRY = 0
Global Const CMD_IGNORE = 1
Global Const CMD_CANCEL = 2
Global Const CMD_CONTINUE = 3

Type ErrorType
    iErrNum As Long
    sHeadline As String
    sProblemMsg As String
    sResponseMsg As String
    sErrorSource As String
    sErrorDescription As String
    iBtnCap(3) As Integer
    iBitmap As Integer
End Type

Global gEStruc As ErrorType
Sub EmptyErrStruc_S(utEStruc As ErrorType)
  Dim i As Integer

  utEStruc.iErrNum = 0
  utEStruc.sHeadline = ""
  utEStruc.sProblemMsg = ""
  utEStruc.sResponseMsg = ""
  utEStruc.sErrorSource = ""
  For i = 0 To 2
    utEStruc.iBtnCap(i) = -1
  Next
  utEStruc.iBitmap = 1

End Sub
Function FillErrorStruct_F(EStruc As ErrorType) As Boolean
  'Must save error text before starting new error handler
  'in case we need it later
  EStruc.sProblemMsg = Error(EStruc.iErrNum)
  On Error GoTo vbDefaultFill

  EStruc.sHeadline = "Error " & Format$(EStruc.iErrNum)
  EStruc.sProblemMsg = EStruc.sErrorDescription
  EStruc.sErrorSource = EStruc.sErrorSource
  EStruc.sResponseMsg = "Contact the Company and tell them you received Error # " & Str$(EStruc.iErrNum) & ". You should write down the program function you were using, the record you were working with, and what you were doing."

   Select Case EStruc.iErrNum
       'Case Error number here
       'not sure what numeric errors user will ecounter, but can be implemented here
       'e.g.
       'EStruc.sHeadline = "Error 3265"
       'EStruc.sResponseMsg = "Contact tech support. Tell them what you were doing in the program."

     Case Else

       EStruc.sHeadline = "Error " & Format$(EStruc.iErrNum) & ": " & EStruc.sErrorDescription
       EStruc.sProblemMsg = EStruc.sErrorDescription

   End Select

   GoTo FillStrucEnd

vbDefaultFill:

  'Error Not on file
  EStruc.sHeadline = "Error " & Format$(EStruc.iErrNum) & ": Contact Tech Support"
  EStruc.sResponseMsg = "Contact the Company and tell them you received Error # " & Str$(EStruc.iErrNum)
FillStrucEnd:

  Exit Function

End Function
Function iErrorHandler_F(utEStruc As ErrorType) As Integer
  Static sCaption(3) As String
  Dim i As Integer
  Dim iMCursor As Integer

  Beep

  'Setup static array
  If Len(sCaption(0)) < 1 Then
    sCaption(CMD_IGNORE) = "&Ignore"
    sCaption(CMD_RETRY) = "&Retry"
    sCaption(CMD_CANCEL) = "&Cancel"
    sCaption(CMD_CONTINUE) = "Continue"
  End If

  Load frmErrors

  'Did caller pass error info?  If not fill struc with the needed info
  If Len(utEStruc.sHeadline) < 1 Then
    i = FillErrorStruct_F(utEStruc)
  End If

  frmErrors!lblHeadline.Caption = utEStruc.sHeadline
  frmErrors!lblProblem.Caption = utEStruc.sProblemMsg
  frmErrors!lblSource.Caption = utEStruc.sErrorSource
  frmErrors!lblResponse.Caption = utEStruc.sResponseMsg

  frmErrors.Show
  iErrorHandler_F = frmErrors.Tag   ' Save user response
  Unload frmErrors                  ' Unload and release form

  EmptyErrStruc_S utEStruc          ' Release memory

End Function

您可能会遇到仅为您的应用程序自定义的错误。这通常是专门针对您的应用程序的错误的简短列表。 如果您还没有常量模块,请创建一个包含自定义错误的 ENUM 的模块。 (注意:Office '97 不支持 ENUMS。)。 ENUM 应该如下所示:

Public Enum CustomErrorName
  MaskedFilterNotSupported
  InvalidMonthNumber
End Enum

创建一个会引发自定义错误的模块。

'********************************************************************************************************************************
'    MODULE: CustomErrorList
'
'   PURPOSE: For trapping custom errors applicable to this application
'
'INSTRUCTIONS:  To use this module to create your own custom error:
'               1.  Add the Name of the Error to the CustomErrorName Enum
'               2.  Add a Case Statement to the raiseCustomError Sub
'               3.  Call the raiseCustomError Sub in the routine you may see the custom error
'               4.  Make sure the routine you call the raiseCustomError has error handling in it
'
'
'     Date:    Name:           Description:
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'03/26/2010    Ray       Initial Creation
'********************************************************************************************************************************
Option Explicit
Const MICROSOFT_OFFSET = 512 'Microsoft reserves error values between vbObjectError and vbObjectError + 512
'************************************************************************************************
'  FUNCTION:  raiseCustomError
'
'   PURPOSE:  Raises a custom error based on the information passed
'
'PARAMETERS:  customError - An integer of type CustomErrorName Enum that defines the custom error
'             errorSource - The place the error came from
'
'   Returns:  The ASCII vaule that should be used for the Keypress
'
'     Date:    Name:           Description:
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'03/26/2010    Ray       Initial Creation
'************************************************************************************************
Public Sub raiseCustomError(customError As Integer, Optional errorSource As String = "")
  Dim errorLong As Long
  Dim errorDescription As String

  errorLong = vbObjectError + MICROSOFT_OFFSET + customError

  Select Case customError

    Case CustomErrorName.MaskedFilterNotSupported
      errorDescription = "The mask filter passed is not supported"

    Case CustomErrorName.InvalidMonthNumber
      errorDescription = "Invalid Month Number Passed"

    Case Else
      errorDescription = "The custom error raised is unknown."

  End Select

  Err.Raise errorLong, errorSource, errorDescription

End Sub

您现在可以很好地捕获程序中的错误。你的子(或函数)应该看起来像这样:

Public Sub MySub(monthNumber as Integer)
  On Error GoTo eh  

  Dim sheetWorkSheet As Worksheet

  'Run Some code here

  '************************************************
  '*   OPTIONAL BLOCK 1:  Look for a specific error
  '************************************************
  'Temporarily Turn off Error Handling so that you can check for specific error
  On Error Resume Next
  'Do some code where you might expect an error.  Example below:
  Const ERR_SHEET_NOT_FOUND = 9 'This error number is actually subscript out of range, but for this example means the worksheet was not found

  Set sheetWorkSheet = Sheets("January")

  'Now see if the expected error exists

  If Err.Number = ERR_SHEET_NOT_FOUND Then
    MsgBox "Hey!  The January worksheet is missing.  You need to recreate it."
    Exit Sub
  ElseIf Err.Number <> 0 Then
    'Uh oh...there was an error we did not expect so just run basic error handling 
    GoTo eh
  End If

  'Finished with predictable errors, turn basic error handling back on:
  On Error GoTo eh

  '**********************************************************************************
  '*   End of OPTIONAL BLOCK 1
  '**********************************************************************************

  '**********************************************************************************
  '*   OPTIONAL BLOCK 2:  Raise (a.k.a. "Throw") a Custom Error if applicable
  '**********************************************************************************
  If not (monthNumber >=1 and monthnumber <=12) then
    raiseCustomError CustomErrorName.InvalidMonthNumber, "My Sub"
  end if
  '**********************************************************************************
  '*   End of OPTIONAL BLOCK 2
  '**********************************************************************************

  'Rest of code in your sub

  goto sub_exit

eh:
  gEStruc.iErrNum = Err.Number
  gEStruc.sErrorDescription = Err.Description
  gEStruc.sErrorSource = Err.Source
  m_rc = iErrorHandler_F(gEStruc)

  If m_rc = CMD_RETRY Then
    Resume
  End If

sub_exit:
  'Any final processing you want to do.
  'Be careful with what you put here because if it errors out, the error rolls up.  This can be difficult to debug; especially if calling routine has no error handling.

  Exit Sub 'I was told a long time ago (10+ years) that exit sub was better than end sub...I can't tell you why, so you may not want to put in this line of code.  It's habit I can't break :P
End Sub

上述代码的复制/粘贴可能无法立即生效,但绝对可以为您提供要点。

顺便说一句,如果您需要我做您的公司徽标,请联系我http://www.MySuperCrappyLogoLabels99.com

【讨论】:

  • 非常感谢这个错误处理程序 :-) 它看起来相当不错,但是对于我正在使用的工具来说,它可能是一个矫枉过正的工具。但仍然..也许我会实现它:-) BTW 徽标很棒 :D 如果我需要这样的标志,我会及时通知你
  • 标志不见了:(
【解决方案3】:

我绝对不会使用 Block1。在与错误无关的 IF 语句中包含错误块似乎不正确。

我猜第 2,3 和 4 块是主题的变体。我更喜欢使用块 3 和块 4 而不是块 2,只是因为不喜欢 GOTO 语句;我一般使用 Block4 方法。这是我用来检查是否添加了 Microsoft ActiveX Data Objects 2.8 库的代码示例,如果没有添加,或者如果 2.8 不可用,则使用早期版本。

Option Explicit
Public booRefAdded As Boolean 'one time check for references

Public Sub Add_References()
Dim lngDLLmsadoFIND As Long

If Not booRefAdded Then
    lngDLLmsadoFIND = 28 ' load msado28.tlb, if cannot find step down versions until found

        On Error GoTo RefErr:
            'Add Microsoft ActiveX Data Objects 2.8
            Application.VBE.ActiveVBProject.references.AddFromFile _
            Environ("CommonProgramFiles") + "\System\ado\msado" & lngDLLmsadoFIND & ".tlb"

        On Error GoTo 0

    Exit Sub

RefErr:
        Select Case Err.Number
            Case 0
                'no error
            Case 1004
                 'Enable Trust Centre Settings
                 MsgBox ("Certain VBA References are not available, to allow access follow these steps" & Chr(10) & _
                 "Goto Excel Options/Trust Centre/Trust Centre Security/Macro Settings" & Chr(10) & _
                 "1. Tick - 'Disable all macros with notification'" & Chr(10) & _
                 "2. Tick - 'Trust access to the VBA project objects model'")
                 End
            Case 32813
                 'Err.Number 32813 means reference already added
            Case 48
                 'Reference doesn't exist
                 If lngDLLmsadoFIND = 0 Then
                    MsgBox ("Cannot Find Required Reference")
                    End
                Else
                    For lngDLLmsadoFIND = lngDLLmsadoFIND - 1 To 0 Step -1
                           Resume
                    Next lngDLLmsadoFIND
                End If

            Case Else
                 MsgBox Err.Number & vbCrLf & Err.Description, vbCritical, "Error!"
                End
        End Select

        On Error GoTo 0
End If
booRefAdded = TRUE
End Sub

【讨论】:

  • 非常感谢您的帮助。并感谢您的示例!检查参考资料是个好主意。我想我会选择块 3 或 4。实际上,如果我使用块 3,我可以继续使用普通代码而不添加错误捕获的进一步语句,还是应该写 On Error Goto 0 ?
  • 也许我应该——因为你也用过它
  • 最后一个“On Error GoTo 0”被忽略。它只有在执行时没有处理错误时才有效。
【解决方案4】:

我保持简单:
在模块级别,我定义了两个变量并将一个设置为模块本身的名称。

    Private Const ThisModuleName            As String = "mod_Custom_Functions"
    Public sLocalErrorMsg                   As String

在模块的每个子/函数中,我定义了一个局部变量

    Dim ThisRoutineName                     As String

我将 ThisRoutineName 设置为子或函数的名称

' Housekeeping
    On Error Goto ERR_RTN
    ThisRoutineName = "CopyWorksheet"

然后我将所有错误发送到 ERR_RTN:当它们发生时,但我首先设置 sLocalErrorMsg 来定义错误的实际含义并提供一些调试信息。

    If Len(Trim(FromWorksheetName)) < 1 Then
        sLocalErrorMsg = "Parameter 'FromWorksheetName' Is Missing."
        GoTo ERR_RTN
    End If

在每个子/函数的底部,我将逻辑流程引导如下

    '
    ' The "normal" logic goes here for what the routine does
    '
    GoTo EXIT_RTN

    ERR_RTN:

        On Error Resume Next

    ' Call error handler if we went this far.
        ErrorHandler ThisModuleName, ThisRoutineName, sLocalErrorMsg, Err.Description, Err.Number, False

    EXIT_RTN:

        On Error Resume Next
     '
     ' Some closing logic
     '
    End If

然后我有一个单独的模块,我在所有项目中都放置了一个名为“mod_Error_Handler”的模块。

    '
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ' Subroutine Name:     ErrorHandler                                                     '
    '                                                                                       '
    ' Description:                                                                          '
    '   This module will handle the common error alerts.                                    '
    '                                                                                       '
    ' Inputs:                                                                               '
    '   ModuleName                String    'The name of the module error is in.            '
    '   RoutineName               String    'The name of the routine error in in.           '
    '   LocalErrorMsg             String    'A local message to assist with troubleshooting.'
    '   ERRDescription            String    'The Windows Error Description.                 '
    '   ERRCode                   Long      'The Windows Error Code.                        '
    '   Terminate                 Boolean   'End program if error encountered?              '
    '                                                                                       '
    ' Revision History:                                                                     '
    ' Date (YYYYMMDD) Author                Change                                          '
    ' =============== ===================== =============================================== '
    ' 20140529        XXXXX X. XXXXX        Original                                        '
    '                                                                                       '
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    '
    Public Sub ErrorHandler(ModuleName As String, RoutineName As String, LocalErrorMsg As String, ERRDescription As String, ERRCode As Long, Terminate As Boolean)
        Dim sBuildErrorMsg                 As String

    ' Build Error Message To Display
        sBuildErrorMsg = "Error Information:" & vbCrLf & vbCrLf

        If Len(Trim(ModuleName)) < 1 Then
            ModuleName = "Unknown"
        End If

        If Len(Trim(RoutineName)) < 1 Then
           RoutineName = "Unknown"
        End If

        sBuildErrorMsg = sBuildErrorMsg & "Module Name:        " & ModuleName & vbCrLf & vbCrLf
        sBuildErrorMsg = sBuildErrorMsg & "Routine Name:       " & RoutineName & vbCrLf & vbCrLf

        If Len(Trim(LocalErrorMsg)) > 0 Then
            sBuildErrorMsg = sBuildErrorMsg & "Local Error Msg:    " & LocalErrorMsg & vbCrLf & vbCrLf
        End If

        If Len(Trim(ERRDescription)) > 0 Then
            sBuildErrorMsg = sBuildErrorMsg & "Program Error Msg:  " & ERRDescription & vbCrLf & vbCrLf
            If IsNumeric(ERRCode) Then
                sBuildErrorMsg = sBuildErrorMsg & "Program Error Code: " & Trim(Str(ERRCode)) & vbCrLf & vbCrLf
            End If
        End If

        MsgBox sBuildErrorMsg, vbOKOnly + vbExclamation, "Error Detected!"

        If Terminate Then
            End
        End If

    End Sub

最终结果是弹出错误消息,告诉我在什么模块,什么子程序,以及错误消息具体是什么。此外,它还会插入Windows错误信息和代码。

【讨论】:

    【解决方案5】:

    块 2 不起作用,因为它没有重置错误处理程序,可能导致无限循环。要使错误处理在 VBA 中正常工作,您需要一个 Resume 语句来清除错误处理程序。 Resume 还会重新激活以前的错误处理程序。 Block 2 失败,因为新的错误会返回到之前的错误处理程序,导致无限循环。

    块 3 失败,因为没有 Resume 语句,因此之后的任何错误处理尝试都将失败。

    每个错误处理程序都必须通过退出过程或Resume 语句来结束。围绕错误处理程序路由正常执行是令人困惑的。这就是错误处理程序通常位于底部的原因。

    但这是处理 VBA 中的错误的另一种方法。它像 VB.net 中的 Try/Catch 一样处理内联错误。有一些陷阱,但如果管理得当,它的效果非常好。

    Sub InLineErrorHandling()
    
        'code without error handling
    
    BeginTry1:
    
        'activate inline error handler
        On Error GoTo ErrHandler1
    
            'code block that may result in an error
            Dim a As String: a = "Abc"
            Dim c As Integer: c = a 'type mismatch
    
    ErrHandler1:
    
        'handle the error
        If Err.Number <> 0 Then
    
            'the error handler has deactivated the previous error handler
    
            MsgBox (Err.Description)
    
            'Resume (or exit procedure) is the only way to get out of an error handling block
            'otherwise the following On Error statements will have no effect
            'CAUTION: it also reactivates the previous error handler
            Resume EndTry1
        End If
    
    EndTry1:
        'CAUTION: since the Resume statement reactivates the previous error handler
        'you must ALWAYS use an On Error GoTo statement here
        'because another error here would cause an endless loop
        'use On Error GoTo 0 or On Error GoTo <Label>
        On Error GoTo 0
    
        'more code with or without error handling
    
    End Sub
    

    来源:

    完成这项工作的关键是使用Resume 语句,然后紧跟另一个On Error 语句。 Resume 位于错误处理程序中,并将代码转移到 EndTry1 标签。您必须立即设置另一个On Error 语句以避免出现问题,因为之前的错误处理程序将“恢复”。也就是说,它将处于活动状态并准备好处理另一个错误。这可能会导致错误重复并进入无限循环。

    为避免再次使用以前的错误处理程序,您需要将On Error 设置为新的错误处理程序,或者只需使用On Error Goto 0 取消所有错误处理程序。

    【讨论】:

      【解决方案6】:

      这就是我明天要教给我的学生的内容。经过多年的研究... 即上面的所有文档http://www.cpearson.com/excel/errorhandling.htm 都被认为是一个优秀的文档...

      我希望这对其他人进行了总结。有一个Err 对象和一个活动(或非活动)ErrorHandler。两者都需要针对新错误进行处理和重置。

      将其粘贴到工作簿中并按 F8 逐步完成。

      Sub ErrorHandlingDemonstration()
      
          On Error GoTo ErrorHandler
      
          'this will error
          Debug.Print (1 / 0)
      
          'this will also error
          dummy = Application.WorksheetFunction.VLookup("not gonna find me", Range("A1:B2"), 2, True)
      
          'silly error
          Dummy2 = "string" * 50
      
          Exit Sub
      
      zeroDivisionErrorBlock:
          maybeWe = "did some cleanup on variables that shouldnt have been divided!"
          ' moves the code execution to the line AFTER the one that errored
          Resume Next
      
      vlookupFailedErrorBlock:
          maybeThisTime = "we made sure the value we were looking for was in the range!"
          ' moves the code execution to the line AFTER the one that errored
          Resume Next
      
      catchAllUnhandledErrors:
          MsgBox(thisErrorsDescription)
          Exit Sub
      
      ErrorHandler:
          thisErrorsNumberBeforeReset = Err.Number
          thisErrorsDescription = Err.Description
          'this will reset the error object and error handling
          On Error GoTo 0
          'this will tell vba where to go for new errors, ie the new ErrorHandler that was previous just reset!
          On Error GoTo ErrorHandler
      
          ' 11 is the err.number for division by 0
          If thisErrorsNumberBeforeReset = 11 Then
              GoTo zeroDivisionErrorBlock
          ' 1004 is the err.number for vlookup failing
          ElseIf thisErrorsNumberBeforeReset = 1004 Then
              GoTo vlookupFailedErrorBlock
          Else
              GoTo catchAllUnhandledErrors
          End If
      
      End Sub
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-09
        • 2015-08-09
        • 1970-01-01
        • 1970-01-01
        • 2017-03-23
        • 1970-01-01
        相关资源
        最近更新 更多