[英]Properly Handling Errors in VBA (Excel)
I've been working with VBA for quite a while now, but I'm still not so sure about Error Handling.我已经使用 VBA 有一段时间了,但我仍然不太确定错误处理。
A good article is the one of CPearson.com一篇好文章是CPearson.com的一篇
However I'm still wondering if the way I used to do ErrorHandling was/is completely wrong: Block 1但是我仍然想知道我以前执行 ErrorHandling 的方式是否完全错误: Block 1
On Error Goto ErrCatcher
If UBound(.sortedDates) > 0 Then
// Code
Else
ErrCatcher:
// Code
End If
The if clause, because if it is true, will be executed and if it fails the Goto will go into the Else-part, since the Ubound of an Array should never be zero or less, without an Error, this method worked quite well so far. if 子句,因为如果它是真的,将被执行,如果它失败,Goto 将进入 Else 部分,因为数组的 Ubound 不应该是零或更小,没有错误,这个方法工作得很好所以远的。
If I understood it right it should be like this:如果我理解正确的话应该是这样的:
Block 2第 2 座
On Error Goto ErrCatcher
If Ubound(.sortedDates) > 0 Then
// Code
End If
Goto hereX
ErrCatcher:
//Code
Resume / Resume Next / Resume hereX
hereX:
Or even like this: Block 3甚至像这样: Block 3
On Error Goto ErrCatcher
If Ubound(.sortedDates) > 0 Then
// Code
End If
ErrCatcher:
If Err.Number <> 0 then
//Code
End If
The most common way I see is that one, that the Error "Catcher" is at the end of a sub and the Sub actually ends before with a "Exit Sub", but however isn't it a little confusing if the Sub is quite big if you jump vice versa to read through the code?我看到的最常见的方式是,错误“Catcher”位于 sub 的末尾,而 Sub 实际上以“Exit Sub”结束,但是如果 Sub 相当如果你反之跳到通读代码,那么大吗?
Block 4第 4 座
Source of the following Code: CPearson.com
以下代码的来源: CPearson.com
On Error Goto ErrHandler:
N = 1 / 0 ' cause an error
'
' more code
'
Exit Sub
ErrHandler:
' error handling code'
Resume Next
End Sub
Should it be like in Block 3 ?应该像第 3 块那样吗?
You've got one truly marvelous answer from ray023, but your comment that it's probably overkill is apt.您从 ray023 那里得到了一个非常了不起的答案,但您认为它可能是矫枉过正的评论是恰当的。 For a "lighter" version....
对于“更轻”的版本....
Block 1 is, IMHO, bad practice.恕我直言,第 1 块是不好的做法。 As already pointed out by osknows, mixing error-handling with normal-path code is Not Good.
正如 osknows 已经指出的那样,将错误处理与正常路径代码混合是不好的。 For one thing, if a new error is thrown while there's an Error condition in effect you will not get an opportunity to handle it (unless you're calling from a routine that also has an error handler, where the execution will "bubble up").
一方面,如果在错误条件生效时抛出新错误,您将没有机会处理它(除非您从也有错误处理程序的例程中调用,执行将“冒泡” )。
Block 2 looks like an imitation of a Try/Catch block.块 2看起来像是对 Try/Catch 块的模仿。 It should be okay, but it's not The VBA Way.
应该没问题,但这不是 VBA 方式。 Block 3 is a variation on Block 2.
Block 3是 Block 2 的变体。
Block 4 is a bare-bones version of The VBA Way. Block 4是 The VBA Way 的基本版本。 I would strongly advise using it, or something like it, because it's what any other VBA programmer inherting the code will expect.
我强烈建议使用它或类似的东西,因为这是任何其他继承代码的 VBA 程序员所期望的。 Let me present a small expansion, though:
不过,让我介绍一个小扩展:
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
Note that second Resume
.请注意第二个
Resume
。 This is a trick I learned recently: It will never execute in normal processing, since the Resume <label>
statement will send the execution elsewhere.这是我最近学到的一个技巧:它永远不会在正常处理中执行,因为
Resume <label>
语句会将执行发送到其他地方。 It can be a godsend for debugging, though.不过,它可能是调试的天赐之物。 When you get an error notification, choose Debug (or press Ctl-Break, then choose Debug when you get the "Execution was interrupted" message).
当您收到错误通知时,选择“调试”(或按 Ctl-Break,然后在收到“执行被中断”消息时选择“调试”)。 The next (highlighted) statement will be either the
MsgBox
or the following statement.下一个(突出显示的)语句将是
MsgBox
或以下语句。 Use "Set Next Statement" (Ctl-F9) to highlight the bare Resume
, then press F8.使用“设置下一条语句”(Ctl-F9)突出显示裸露的
Resume
,然后按 F8。 This will show you exactly where the error was thrown.这将向您准确显示错误发生的位置。
As to your objection to this format "jumping around", A) it's what VBA programmers expect, as stated previously, & B) your routines should be short enough that it's not far to jump.至于您对这种“跳跃”格式的反对,A)这是 VBA 程序员所期望的,如前所述,& B)您的例程应该足够短,以至于跳跃不远。
Two main purposes for error handling:错误处理的两个主要目的:
So, how would you do this?那么,你会怎么做呢?
First of all, create an error form to display when an unexpected error occurs.首先,创建一个错误表单以在发生意外错误时显示。
It could look something like this (FYI: Mine is called frmErrors):它可能看起来像这样(仅供参考:我的称为 frmErrors):
Notice the following labels:请注意以下标签:
Also, the standard command buttons:此外,标准命令按钮:
There's nothing spectacular in the code for this form:这种形式的代码没有什么特别之处:
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
Basically, you want to know which button the user pressed when the form closes.基本上,您想知道表单关闭时用户按下了哪个按钮。
Next, create an Error Handler Module that will be used throughout your VBA app:接下来,创建一个将在整个 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
You may have errors that will be custom only to your application.您可能会遇到仅适用于您的应用程序的错误。 This would typically be a short list of errors specifically only to your application.
这通常是专门针对您的应用程序的错误的简短列表。 If you don't already have a constants module, create one that will contain an ENUM of your custom errors.
如果您还没有常量模块,请创建一个包含自定义错误的 ENUM 的模块。 (NOTE: Office '97 does NOT support ENUMS.).
(注意:Office '97 不支持 ENUMS。)。 The ENUM should look something like this:
ENUM 应该是这样的:
Public Enum CustomErrorName
MaskedFilterNotSupported
InvalidMonthNumber
End Enum
Create a module that will throw your custom errors.创建一个会抛出您的自定义错误的模块。
'********************************************************************************************************************************
' 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
You are now well equipped to trap errors in your program.您现在可以很好地捕获程序中的错误。 You sub (or function), should look something like this:
您的子(或函数)应如下所示:
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
A copy/paste of the code above may not work right out of the gate, but should definitely give you the gist.上面代码的复制/粘贴可能无法立即生效,但绝对可以为您提供要点。
I definitely wouldn't use Block1.我绝对不会使用 Block1。 It doesn't seem right having the Error block in an IF statement unrelated to Errors.
在与错误无关的 IF 语句中包含错误块似乎不正确。
Blocks 2,3 & 4 I guess are variations of a theme.我猜第 2,3 和 4 块是主题的变体。 I prefer the use of Blocks 3 & 4 over 2 only because of a dislike of the GOTO statement;
我更喜欢使用块 3 和块 4 而不是块 2,只是因为不喜欢 GOTO 语句; I generally use the Block4 method.
我一般使用 Block4 方法。 This is one example of code I use to check if the Microsoft ActiveX Data Objects 2.8 Library is added and if not add or use an earlier version if 2.8 is not available.
这是我用来检查是否添加了 Microsoft ActiveX 数据对象 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
I keep things simple:我保持简单:
At the module level I define two variables and set one to the name of the module itself.在模块级别,我定义了两个变量并将一个设置为模块本身的名称。
Private Const ThisModuleName As String = "mod_Custom_Functions"
Public sLocalErrorMsg As String
Within each Sub/Function of the module I define a local variable在模块的每个子/功能中,我定义了一个局部变量
Dim ThisRoutineName As String
I set ThisRoutineName to the name of the sub or function我将 ThisRoutineName 设置为子或函数的名称
' Housekeeping
On Error Goto ERR_RTN
ThisRoutineName = "CopyWorksheet"
I then send all errors to an ERR_RTN: when they occur, but I first set the sLocalErrorMsg to define what the error actually is and provide some debugging info.然后,我将所有错误发送到 ERR_RTN:当它们发生时,但我首先设置 sLocalErrorMsg 以定义错误的实际含义并提供一些调试信息。
If Len(Trim(FromWorksheetName)) < 1 Then
sLocalErrorMsg = "Parameter 'FromWorksheetName' Is Missing."
GoTo ERR_RTN
End If
At the bottom of each sub/function, I direct the logic flow as follows在每个子/功能的底部,我将逻辑流程指导如下
'
' 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
I then have a seperate module I put in all projects called "mod_Error_Handler".然后我有一个单独的模块,我把它放在所有名为“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
The end result is a pop-up error message teling me in what module, what soubroutine, and what the error message specifically was.最终结果是弹出错误消息,告诉我在什么模块,什么子程序,以及错误消息具体是什么。 In addition, it also will insert the Windows error message and code.
此外,它还会插入 Windows 错误消息和代码。
Block 2 doesn't work because it doesn't reset the Error Handler potentially causing an endless loop.块 2 不起作用,因为它不会重置错误处理程序,可能会导致无限循环。 For Error Handling to work properly in VBA, you need a
Resume
statement to clear the Error Handler.要使错误处理在 VBA 中正常工作,您需要一个
Resume
语句来清除错误处理程序。 The Resume
also reactivates the previous Error Handler. Resume
还会重新激活先前的错误处理程序。 Block 2 fails because a new error would go back to the previous Error Handler causing an infinite loop.块 2 失败,因为新错误将返回到先前的错误处理程序,从而导致无限循环。
Block 3 fails because there is no Resume
statement so any attempt at error handling after that will fail.块 3 失败,因为没有
Resume
语句,因此之后的任何错误处理尝试都将失败。
Every error handler must be ended by exiting the procedure or a Resume
statement.每个错误处理程序都必须通过退出过程或
Resume
语句来结束。 Routing normal execution around an error handler is confusing.围绕错误处理程序路由正常执行是令人困惑的。 This is why error handlers are usually at the bottom.
这就是错误处理程序通常位于底部的原因。
But here is another way to handle an error in VBA.但这是另一种处理 VBA 错误的方法。 It handles the error inline like Try/Catch in VB.net There are a few pitfalls, but properly managed it works quite nicely.
它可以像 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
Sources:资料来源:
The key to making this work is to use a Resume
statement immediately followed by another On Error
statement.完成这项工作的关键是使用
Resume
语句,然后紧跟另一个On Error
语句。 The Resume
is within the error handler and diverts code to the EndTry1
label. Resume
位于错误处理程序中,并将代码转移到EndTry1
标签。 You must immediately set another On Error
statement to avoid problems as the previous error handler will "resume".您必须立即设置另一个
On Error
语句以避免出现问题,因为之前的错误处理程序将“恢复”。 That is, it will be active and ready to handle another error.也就是说,它将处于活动状态并准备好处理另一个错误。 That could cause the error to repeat and enter an infinite loop.
这可能会导致错误重复并进入无限循环。
To avoid using the previous error handler again you need to set On Error
to a new error handler or simply use On Error Goto 0
to cancel all error handling.为了避免再次使用以前的错误处理程序,您需要将
On Error
设置为新的错误处理程序,或者简单地使用On Error Goto 0
取消所有错误处理。
This is what I'm teaching my students tomorrow.这就是我明天要教给我的学生的东西。 After years of looking at this stuff... ie all of the documentation above http://www.cpearson.com/excel/errorhandling.htm comes to mind as an excellent one...
经过多年查看这些东西......即上面的所有文档http://www.cpearson.com/excel/errorhandling.htm想到一个优秀的......
I hope this summarizes it for others.我希望这对其他人进行了总结。 There is an
Err
object and an active (or inactive) ErrorHandler
.有一个
Err
对象和一个活动(或非活动) ErrorHandler
。 Both need to be handled and reset for new errors.两者都需要针对新错误进行处理和重置。
Paste this into a workbook and step through it with F8.将其粘贴到工作簿中,然后按 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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.