In this Article
- VBA Errors Cheat Sheet
- Errors
- VBA Error Handling
- VBA On Error Statement
- On Error GoTo 0
- On Error Resume Next
- Err.Number, Err.Clear, and Catching Errors
- On Error GoTo Line
- VBA IsError
- If Error VBA
- VBA Error Types
- Runtime Errors
- Syntax Errors
- Compile Errors
- Debug > Compile
- OverFlow Error
- Other VBA Error Terms
- VBA Catch Error
- VBA Ignore Error
- VBA Throw Error / Err.Raise
- VBA Error Trapping
- VBA Error Message
- VBA Error Handling in a Loop
- VBA Error Handling in Access
VBA Errors Cheat Sheet
Errors
On Error – Stop code and display error
On Error Goto 0
On Error – Skip error and continue running
On Error Resume Next
On Error – Go to a line of code [Label]
On Error Goto [Label]
Clears (Resets) Error
On Error GoTo –1
Show Error number
MsgBox Err.Number
Show Description of error
MsgBox Err.Description
Function to generate own error
Err.Raise
See more VBA “Cheat Sheets” and free PDF Downloads
VBA Error Handling
VBA Error Handling refers to the process of anticipating, detecting, and resolving VBA Runtime Errors. The VBA Error Handling process occurs when writing code, before any errors actually occur.
VBA Runtime Errors are errors that occur during code execution. Examples of runtime errors include:
- Referencing a non-existent workbook, worksheet, or other object (Run-time Error 1004)
- Invalid data ex. referencing an Excel cell containing an error (Type Mismatch – Run-time Error 13)
- Attempting to divide by zero
VBA On Error Statement
Most VBA error handling is done with the On Error Statement. The On Error statement tells VBA what to do if it encounters an error. There are three On Error Statements:
- On Error GoTo 0
- On Error Resume Next
- On Error GoTo Line
On Error GoTo 0
On Error GoTo 0 is VBA’s default setting. You can restore this default setting by adding the following line of code:
On Error GoTo 0
When an error occurs with On Error GoTo 0, VBA will stop executing code and display its standard error message box.
Often you will add an On Error GoTo 0 after adding On Error Resume Next error handling (next section):
Sub ErrorGoTo0()
On Error Resume Next
ActiveSheet.Shapes("Start_Button").Delete
On Error GoTo 0
'Run More Code
End Sub
On Error Resume Next
On Error Resume Next tells VBA to skip any lines of code containing errors and proceed to the next line.
On Error Resume Next
Note: On Error Resume Next does not fix an error, or otherwise resolve it. It simply tells VBA to proceed as if the line of code containing the error did not exist. Improper use of On Error Resume Next can result in unintended consequences.
A great time to use On Error Resume Next is when working with objects that may or may not exist. For example, you want to write some code that will delete a shape, but if you run the code when the shape is already deleted, VBA will throw an error. Instead you can use On Error Resume Next to tell VBA to delete the shape if it exists.
On Error Resume Next
ActiveSheet.Shapes("Start_Button").Delete
On Error GoTo 0
Notice we added On Error GoTo 0 after the line of code containing the potential error. This resets the error handling.
In the next section we’ll show you how to test if an error occurred using Err.Number, giving you more advanced error handling options.
VBA Coding Made Easy
Stop searching for VBA code online. Learn more about AutoMacro — A VBA Code Builder that allows beginners to code procedures from scratch with minimal coding knowledge and with many time-saving features for all users!
Learn More
Err.Number, Err.Clear, and Catching Errors
Instead of simply skipping over a line containing an error, we can catch the error by using On Error Resume Next and Err.Number.
Err.Number returns an error number corresponding with the type of error detected. If there is no error, Err.Number = 0.
For example, this procedure will return “11” because the error that occurs is Run-time error ’11’.
Sub ErrorNumber_ex()
On Error Resume Next
ActiveCell.Value = 2 / 0
MsgBox Err.Number
End Sub
Error Handling with Err.Number
The true power of Err.Number lies in the ability to detect if an error occurred (Err.Number <> 0). In the example below, we’ve created a function that will test if a sheet exists by using Err.Number.
Sub TestWS()
MsgBox DoesWSExist("test")
End Sub
Function DoesWSExist(wsName As String) As Boolean
Dim ws As Worksheet
On Error Resume Next
Set ws = Sheets(wsName)
'If Error WS Does not exist
If Err.Number <> 0 Then
DoesWSExist = False
Else
DoesWSExist = True
End If
On Error GoTo -1
End Function
Note: We’ve added a On Error GoTo -1 to the end which resets Err.Number to 0 (see two sections down).
With On Error Resume Next and Err.Number, you can replicate the “Try” & “Catch” functionality of other programming languages.
On Error GoTo Line
On Error GoTo Line tells VBA to “go to” a labeled line of code when an error is encountered. You declare the Go To statement like this (where errHandler is the line label to go to):
On Error GoTo errHandler
and create a line label like this:
errHandler:
Note: This is the same label that you’d use with a regular VBA GoTo Statement.
Below we will demonstrate using On Error GoTo Line to Exit a procedure.
On Error Exit Sub
You can use On Error GoTo Line to exit a sub when an error occurs.
You can do this by placing the error handler line label at the end of your procedure:
Sub ErrGoToEnd()
On Error GoTo endProc
'Some Code
endProc:
End Sub
or by using the Exit Sub command:
Sub ErrGoToEnd()
On Error GoTo endProc
'Some Code
GoTo skipExit
endProc:
Exit Sub
skipExit:
'Some More Code
End Sub
Err.Clear, On Error GoTo -1, and Resetting Err.Number
After an error is handled, you should generally clear the error to prevent future issues with error handling.
After an error occurs, both Err.Clear and On Error GoTo -1 can be used to reset Err.Number to 0. But there is one very important difference: Err.Clear does not reset the actual error itself, it only resets the Err.Number.
What does that mean? Using Err.Clear, you will not be able to change the error handling setting. To see the difference, test out this code and replace On Error GoTo -1 with Err.Clear:
Sub ErrExamples()
On Error GoTo errHandler:
'"Application-defined" error
Error (13)
Exit Sub
errHandler:
' Clear Error
On Error GoTo -1
On Error GoTo errHandler2:
'"Type mismatch" error
Error (1034)
Exit Sub
errHandler2:
Debug.Print Err.Description
End Sub
Typically, I recommend always using On Error GoTo -1, unless you have a good reason to use Err.Clear instead.
VBA On Error MsgBox
You might also want to display a Message Box on error. This example will display different message boxes depending on where the error occurs:
Sub ErrorMessageEx()
Dim errMsg As String
On Error GoTo errHandler
'Stage 1
errMsg = "An error occured during the Copy & Paste stage."
'Err.Raise (11)
'Stage 2
errMsg = "An error occured during the Data Validation stage."
'Err.Raise (11)
'Stage 3
errMsg = "An error occured during the P&L-Building and Copy-Over stage."
Err.Raise (11)
'Stage 4
errMsg = "An error occured while attempting to log the Import on the Setup Page"
'Err.Raise (11)
GoTo endProc
errHandler:
MsgBox errMsg
endProc:
End Sub
Here you would replace Err.Raise(11) with your actual code.
VBA IsError
Another way to handle errors is to test for them with the VBA ISERROR Function. The ISERROR Function tests an expression for errors, returning TRUE or FALSE if an error occurs.
Sub IsErrorEx()
MsgBox IsError(Range("a7").Value)
End Sub
VBA Programming | Code Generator does work for you!
If Error VBA
You can also handle errors in VBA with the Excel IFERROR Function. The IFERROR Function must be accessed by using the WorksheetFunction Class:
Sub IfErrorEx()
Dim n As Long
n = WorksheetFunction.IfError(Range("a10").Value, 0)
MsgBox n
End Sub
This will output the value of Range A10, if the value is an error, it will output 0 instead.
VBA Error Types
Runtime Errors
As stated above:
VBA Runtime Errors are errors that occur during code execution. Examples of runtime errors include:
- Referencing a non-existent workbook, worksheet, or other object
- Invalid data ex. referencing an Excel cell containing an error
- Attempting to divide by zero
You can “error handle” runtime errors using the methods discussed above.
Syntax Errors
VBA Syntax Errors are errors with code writing. Examples of syntax errors include:
- Mispelling
- Missing or incorrect punctuation
The VBA Editor identifies many syntax errors with red highlighting:
The VBA Editor also has an option to “Auto Syntax Check”:
When this is checked, the VBA Editor will generate a message box alerting you syntax errors after you enter a line of code:
I personally find this extremely annoying and disable the feature.
Compile Errors
Before attempting to run a procedure, VBA will “compile” the procedure. Compiling transforms the program from source code (that you can see) into executable form (you can’t see).
VBA Compile Errors are errors that prevent the code from compiling.
A good example of a compile error is a missing variable declaration:
Other examples include:
- For without Next
- Select without End Select
- If without End If
- Calling a procedure that does not exist
Syntax Errors (previous section) are a subset of Compile Errors.
AutoMacro | Ultimate VBA Add-in | Click for Free Trial!
Debug > Compile
Compile errors will appear when you attempt to run a Procedure. But ideally, you would identify compile errors prior to attempting to run the procedure.
You can do this by compiling the project ahead of time. To do so, go to Debug > Compile VBA Project.
The compiler will “go to” the first error. Once you fix that error, compile the project again. Repeat until all errors are fixed.
You can tell that all errors are fixed because Compile VBA Project will be grayed out:
OverFlow Error
The VBA OverFlow Error occurs when you attempt to put a value into a variable that is too large. For example, Integer Variables can only contain values between -32,768 to 32,768. If you enter a larger value, you’ll receive an Overflow error:
Instead, you should use the Long Variable to store the larger number.
Other VBA Error Terms
VBA Catch Error
Unlike other programming languages, In VBA there is no Catch Statement. However, you can replicate a Catch Statement by using On Error Resume Next and If Err.Number <> 0 Then. This is covered above in Error Handling with Err.Number.
AutoMacro | Ultimate VBA Add-in | Click for Free Trial!
VBA Ignore Error
To ignore errors in VBA, simply use the On Error Resume Next statement:
On Error Resume Next
However, as mentioned above, you should be careful using this statement as it doesn’t fix an error, it just simply ignores the line of code containing the error.
VBA Throw Error / Err.Raise
To through an error in VBA, you use the Err.Raise method.
This line of code will raise Run-time error ’13’: Type mismatch:
Err.Raise (13)
VBA Error Trapping
VBA Error Trapping is just another term for VBA Error Handling.
VBA Error Message
A VBA Error Message looks like this:
When you click ‘Debug’, you’ll see the line of code that is throwing the error:
AutoMacro | Ultimate VBA Add-in | Click for Free Trial!
VBA Error Handling in a Loop
The best way to error handle within a Loop is by using On Error Resume Next along with Err.Number to detect if an error has occurred (Remember to use Err.Clear to clear the error after each occurrence).
The example below will divide two numbers (Column A by Column B) and output the result into Column C. If there’s an error, the result will be 0.
Sub test()
Dim cell As Range
On Error Resume Next
For Each cell In Range("a1:a10")
'Set Cell Value
cell.Offset(0, 2).Value = cell.Value / cell.Offset(0, 1).Value
'If Cell.Value is Error then Default to 0
If Err.Number <> 0 Then
cell.Offset(0, 2).Value = 0
Err.Clear
End If
Next
End Sub
VBA Error Handling in Access
All of the above examples work exactly the same in Access VBA as in Excel VBA.
Function DelRecord(frm As Form)
'this function is used to delete a record in a table from a form
On Error GoTo ending
With frm
If .NewRecord Then
.Undo
Exit Function
End If
End With
With frm.RecordsetClone
.Bookmark = frm.Bookmark
.Delete
frm.Requery
End With
Exit Function
ending:
End
End Function
Избегание условий ошибки
Когда возникает ошибка времени выполнения, хороший код должен ее обрабатывать. Лучшей стратегией обработки ошибок является запись кода, который проверяет условия ошибки и просто избегает выполнения кода, который приводит к ошибке выполнения.
Одним из ключевых элементов сокращения ошибок во время выполнения является запись небольших процедур, которые делают одно . Чем меньше процедур процедур приходится терпеть неудачу, тем проще код в целом — отлаживать.
Избежать ошибки времени выполнения 91 — Объект или С заблокированной переменной блока:
Эта ошибка будет повышена, если объект используется до назначения ссылки. Возможно, у вас есть процедура, которая получает параметр объекта:
Private Sub DoSomething(ByVal target As Worksheet)
Debug.Print target.Name
End Sub
Если target
не назначена ссылка, приведенный выше код вызовет ошибку, которую легко избежать, проверяя, содержит ли объект фактическую ссылку на объект:
Private Sub DoSomething(ByVal target As Worksheet)
If target Is Nothing Then Exit Sub
Debug.Print target.Name
End Sub
Если target
назначению не присвоена ссылка, то непризнанная ссылка никогда не используется, и ошибка не возникает.
Этот способ раннего выхода из процедуры, когда один или несколько параметров недопустимы, называется предложением охраны .
Избегайте ошибки времени выполнения 9 — Подкласс вне диапазона:
Эта ошибка возникает при доступе к массиву за пределами его границ.
Private Sub DoSomething(ByVal index As Integer)
Debug.Print ActiveWorkbook.Worksheets(index)
End Sub
Учитывая, что индекс больше, чем количество листов в ActiveWorkbook
, приведенный выше код вызовет ошибку времени выполнения. Простое предложение охраны может избежать этого:
Private Sub DoSomething(ByVal index As Integer)
If index > ActiveWorkbook.Worksheets.Count Or index <= 0 Then Exit Sub
Debug.Print ActiveWorkbook.Worksheets(index)
End Sub
Большинство ошибок времени выполнения можно избежать, тщательно проверив значения, которые мы используем, прежде чем мы их используем, и разветвляемся на другом пути выполнения, соответственно, используя простой оператор If
— в сторожевых предложениях, который не делает предположений и не проверяет параметры процедуры, или даже в тело более крупных процедур.
Оператор Error
Даже с защитными пунктами, один не может реально всегда учитывать все возможные ошибки , которые могут быть подняты в теле процедуры. Оператор On Error GoTo
инструктирует VBA перейти к метке линии и ввести «режим обработки ошибок» всякий раз, когда во время выполнения происходит непредвиденная ошибка. После обработки ошибки, код может возобновить обратно в «нормальное» исполнение с помощью Resume
ключевое слово.
Линейные метки обозначают подпрограммы : потому что подпрограммы исходят из устаревшего кода BASIC и используют GoSub
GoTo
и GoSub
и 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 поддерживает номера строк в стиле legacy (например, QBASIC). Скрытое свойство Erl
можно использовать для идентификации номера строки, которая вызвала последнюю ошибку. Если вы не используете номера строк, 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
Номер строки не так актуален, как утверждение, вызвавшее ошибку, и строки нумерации быстро становятся утомительными и не совсем удобны в обслуживании.
Резюме ключевого слова
Подпрограмма обработки ошибок будет либо:
- выполняются до конца процедуры, и в этом случае выполнение возобновляется в процедуре вызова.
- или используйте ключевое слово
Resume
для возобновления выполнения внутри той же процедуры.
Ключевое слово Resume
должно использоваться только в подпрограмме обработки ошибок, потому что если VBA встречает Resume
не находясь в состоянии ошибки, возникает ошибка времени выполнения 20 «Возобновить без ошибок».
Существует несколько способов, по которым подпрограмма обработки ошибок может использовать ключевое слово Resume
:
-
Resume
используется отдельно, выполнение продолжается в инструкции, вызвавшей ошибку . Если ошибка на самом деле не обрабатывается , прежде чем делать это, то та же ошибка будет поднят снова, и выполнение может войти в бесконечный цикл. -
Resume Next
продолжает выполнение инструкции сразу после инструкции, вызвавшей ошибку. Если ошибка на самом деле не обрабатывается , прежде чем делать это, то выполнение разрешается продолжать с потенциально недействительными данными, которые могут привести к логическим ошибкам и неожиданному поведению. -
Resume [line label]
продолжает выполнение на указанной метке строки (или номер строки, если вы используете номера строк в стиле устаревшего стиля). Обычно это позволяет выполнить некоторый код очистки до того, как будет чисто выйти из процедуры, например, чтобы закрыть соединение с базой данных, прежде чем вернуться к вызывающему.
Вкл.
Сам оператор On Error
может использовать ключевое слово Resume
чтобы проинструктировать среду выполнения VBA для эффективного игнорирования всех ошибок .
Если ошибка не выполняется до этого, то выполнение разрешено продолжать с потенциально недействительными данными, что может привести к логическим ошибкам и неожиданному поведению .
Вышеупомянутый акцент не может быть особо подчеркнут. On Error Resume Next эффективно игнорирует все ошибки и выталкивает их под ковер . Программа, которая взрывается с ошибкой во время выполнения с учетом недопустимого ввода, — это более эффективная программа, чем программа, которая работает с неизвестными / непреднамеренными данными — будь то только потому, что ошибка намного легче идентифицируется. On Error Resume Next
можно легко скрыть ошибки .
Оператор On Error
является областью действия процедур — поэтому в данной процедуре обычно должен быть только один , такой оператор On Error
.
Однако иногда не удается избежать ошибки, и переключение на подпрограмму обработки ошибок только на Resume Next
просто не кажется правильным. В этом конкретном случае утверждение с известным до невозможности может быть обернуто между двумя On Error
:
On Error Resume Next
[possibly-failing statement]
Err.Clear 'resets current error
On Error GoTo 0
Команда On Error GoTo 0
сбрасывает обработку ошибок в текущей процедуре, так что любая дополнительная инструкция, вызывающая ошибку времени выполнения , будет необработанной внутри этой процедуры и вместо этого будет переходить в стек вызовов до тех пор, пока она не будет захвачена активным обработчиком ошибок. Если в стеке вызовов нет активного обработчика ошибок, он будет рассматриваться как необработанное исключение.
Public Sub Caller()
On Error GoTo Handler
Callee
Exit Sub
Handler:
Debug.Print "Error " & Err.Number & " in Caller."
End Sub
Public Sub Callee()
On Error GoTo Handler
Err.Raise 1 'This will be handled by the Callee handler.
On Error GoTo 0 'After this statement, errors are passed up the stack.
Err.Raise 2 'This will be handled by the Caller handler.
Exit Sub
Handler:
Debug.Print "Error " & Err.Number & " in Callee."
Resume Next
End Sub
Пользовательские ошибки
Часто при написании специализированного класса вы хотите, чтобы он поднимал свои собственные конкретные ошибки, и вам понадобится чистый способ для кода пользователя / вызова для обработки этих пользовательских ошибок. Оптимальным способом достижения этого является определение специального типа Enum
:
Option Explicit
Public Enum FoobarError
Err_FooWasNotBarred = vbObjectError + 1024
Err_BarNotInitialized
Err_SomethingElseHappened
End Enum
Используя встроенную константу vbObjectError
пользовательские коды ошибок не перекрываются с зарезервированными / существующими кодами ошибок. Необходимо явно указать только первое значение перечисления, поскольку базовое значение каждого члена Enum
1
больше, чем предыдущий элемент, поэтому базовое значение Err_BarNotInitialized
неявно является vbObjectError + 1025
.
Повышение собственных ошибок времени выполнения
Ошибка выполнения может быть повышена с Err.Raise
оператора Err.Raise
, поэтому пользовательская ошибка Err_FooWasNotBarred
может быть повышена следующим образом:
Err.Raise Err_FooWasNotBarred
Метод Err.Raise
также может принимать пользовательские параметры Description
и Source
— по этой причине рекомендуется также определять константы для хранения каждого пользовательского описания ошибки:
Private Const Msg_FooWasNotBarred As String = "The foo was not barred."
Private Const Msg_BarNotInitialized As String = "The bar was not initialized."
А затем создайте выделенный частный метод для повышения каждой ошибки:
Private Sub OnFooWasNotBarredError(ByVal source As String)
Err.Raise Err_FooWasNotBarred, source, Msg_FooWasNotBarred
End Sub
Private Sub OnBarNotInitializedError(ByVal source As String)
Err.Raise Err_BarNotInitialized, source, Msg_BarNotInitialized
End Sub
После этого реализация класса может просто вызвать эти специализированные процедуры для повышения ошибки:
Public Sub DoSomething()
'raises the custom 'BarNotInitialized' error with "DoSomething" as the source:
If Me.Bar Is Nothing Then OnBarNotInitializedError "DoSomething"
'...
End Sub
Клиентский код может обрабатывать Err_BarNotInitialized
как и любую другую ошибку, внутри своей собственной подпрограммы обработки ошибок.
Примечание: наследие Error
ключевое слово также может быть использован вместо Err.Raise
, но это устаревшее / осуждается.
# Avoiding error conditions
When a runtime error occurs, good code should handle it. The best error handling strategy is to write code that checks for error conditions and simply avoids executing code that results in a runtime error.
One key element in reducing runtime errors, is writing small procedures that do one thing. The fewer reasons procedures have to fail, the easier the code as a whole is to debug.
Avoiding runtime error 91 — Object or With block variable not set:
This error will be raised when an object is used before its reference is assigned. One might have a procedure that receives an object parameter:
If target
isn’t assigned a reference, the above code will raise an error that is easily avoided by checking if the object contains an actual object reference:
If target
isn’t assigned a reference, then the unassigned reference is never used, and no error occurs.
This way of early-exiting a procedure when one or more parameter isn’t valid, is called a guard clause.
Avoiding runtime error 9 — Subscript out of range:
This error is raised when an array is accessed outside of its boundaries.
Given an index greater than the number of worksheets in the ActiveWorkbook
, the above code will raise a runtime error. A simple guard clause can avoid that:
Most runtime errors can be avoided by carefully verifying the values we’re using before we use them, and branching on another execution path accordingly using a simple If
statement — in guard clauses that makes no assumptions and validates a procedure’s parameters, or even in the body of larger procedures.
# Custom Errors
Often when writing a specialized class, you’ll want it to raise its own specific errors, and you’ll want a clean way for user/calling code to handle these custom errors. A neat way to achieve this is by defining a dedicated Enum
type:
Using the vbObjectError
built-in constant ensures the custom error codes don’t overlap with reserved/existing error codes. Only the first enum value needs to be explicitly specified, for the underlying value of each Enum
member is 1
greater than the previous member, so the underlying value of Err_BarNotInitialized
is implicitly vbObjectError + 1025
.
# Raising your own runtime errors
A runtime error can be raised using the Err.Raise
statement, so the custom Err_FooWasNotBarred
error can be raised as follows:
The Err.Raise
method can also take custom Description
and Source
parameters — for this reason it’s a good idea to also define constants to hold each custom error’s description:
And then create a dedicated private method to raise each error:
The class’ implementation can then simply call these specialized procedures to raise the error:
The client code can then handle Err_BarNotInitialized
as it would any other error, inside its own error-handling subroutine.
Note: the legacy Error
keyword can also be used in place of Err.Raise
, but it’s obsolete/deprecated.
# Resume keyword
An error-handling subroutine will either:
- run to the end of the procedure, in which case execution resumes in the calling procedure.
- or, use the
Resume
keyword to resume execution inside the same procedure.
The Resume
keyword should only ever be used inside an error handling subroutine, because if VBA encounters Resume
without being in an error state, runtime error 20 «Resume without error» is raised.
There are several ways an error-handling subroutine may use the Resume
keyword:
Resume
used alone, execution continues on the statement that caused the error. If the error isn’t actually handled before doing that, then the same error will be raised again, and execution might enter an infinite loop.Resume Next
continues execution on the statement immediately following the statement that caused the error. If the error isn’t actually handled before doing that, then execution is permitted to continue with potentially invalid data, which may result in logical errors and unexpected behavior.Resume [line label]
continues execution at the specified line label (or line number, if you’re using legacy-style line numbers). This would typically allow executing some cleanup code before cleanly exiting the procedure, such as ensuring a database connection is closed before returning to the caller.
# On Error Resume Next
The On Error
statement itself can use the Resume
keyword to instruct the VBA runtime to effectively ignore all errors.
If the error isn’t actually handled before doing that, then execution is permitted to continue with potentially invalid data, which may result in logical errors and unexpected behavior.
The emphasis above cannot be emphasized enough. On Error Resume Next effectively ignores all errors and shoves them under the carpet. A program that blows up with a runtime error given invalid input is a better program than one that keeps running with unknown/unintended data — be it only because the bug is much more easily identifiable. On Error Resume Next
can easily hide bugs.
The On Error
statement is procedure-scoped — that’s why there should normally be only one, single such On Error
statement in a given procedure.
However sometimes an error condition can’t quite be avoided, and jumping to an error-handling subroutine only to Resume Next
just doesn’t feel right. In this specific case, the known-to-possibly-fail statement can be wrapped between two On Error
statements:
The On Error GoTo 0
instruction resets error handling in the current procedure, such that any further instruction causing a runtime error would be unhandled within that procedure and instead passed up the call stack until it is caught by an active error handler. If there is no active error handler in the call stack, it will be treated as an unhandled exception.
# On Error statement
Even with guard clauses, one cannot realistically always account for all possible error conditions that could be raised in the body of a procedure. The On Error GoTo
statement instructs VBA to jump to a line label and enter «error handling mode» whenever an unexpected error occurs at runtime. After handling an error, code can resume back into «normal» execution using the Resume
keyword.
Line labels denote subroutines: because subroutines originate from legacy BASIC code and uses GoTo
and GoSub
jumps and Return
statements to jump back to the «main» routine, it’s fairly easy to write hard-to-follow spaghetti code if things aren’t rigorously structured. For this reason, it’s best that:
- a procedure has one and only one error-handling subroutine
- the error-handling subroutine only ever runs in an error state
This means a procedure that handles its errors, should be structured like this:
# Error Handling Strategies
Sometimes you want to handle different errors with different actions. In that case you will inspect the global Err
object, which will contain information about the error that was raised — and act accordingly:
As a general guideline, consider turning on the error handling for entire subroutine or function, and handle all the errors that may occur within its scope. If you need to only handle errors in the small section section of the code — turn error handling on and off a the same level:
# Line numbers
VBA supports legacy-style (e.g. QBASIC) line numbers. The Erl
hidden property can be used to identify the line number that raised the last error. If you’re not using line numbers, Erl
will only ever return 0.
If you are using line numbers, but not consistently, then Erl
will return the last line number before the instruction that raised the error.
Keep in mind that Erl
also only has Integer
precision, and will silently overflow. This means that line numbers outside of the integer range (opens new window) will give incorrect results:
The line number isn’t quite as relevant as the statement that caused the error, and numbering lines quickly becomes tedious and not quite maintenance-friendly.
Содержание
- 1 An Example of Code Without Error Handling
- 2 An Example of Error Handling Using the On Error GoTo Statement
- 3 A Simple Error-Handling Routine
- 4 EBEngine.Errors
- 5 error handling by checking the Error code
- 6 Ignoring an Error and Continuing Execution
- 7 Looking Up the Call Stack for a Previous Error Handler
- 8 Placing a Resume Next Statement in Your Error Handler
- 9 Read user choice when dealing with error
- 10 Using Resume Conditionally Based on User Feedback
- 11 Using the Resume <LineLabel> Statement to Specify Where Execution Continues After an Error Occurs
An Example of Code Without Error Handling
<source lang="vb">
Sub cmdNoErrorHandler()
Call TestError1(1, 0)
End Sub
Sub TestError1(Numerator As Integer, Denominator As Integer)
Debug.Print Numerator / Denominator msgBox "I am in Test Error"
End Sub
</source>
An Example of Error Handling Using the On Error GoTo Statement
<source lang="vb">
Sub SimpleErrorHandler()
On Error GoTo SimpleErrorHandler_Err Dim sngResult As Single sngResult = 1 / 0 Exit Sub
SimpleErrorHandler_Err:
msgBox "Oops!" Exit Sub
End Sub
</source>
A Simple Error-Handling Routine
<source lang="vb">
Sub TestError2()
On Error GoTo TestError2_Err
Debug.Print 1 / 0 msgBox "I am in Test Error" Exit Sub
TestError2_Err:
If Err = 11 Then msgBox "Variable 2 Cannot Be a Zero", , "Custom Error Handler" End If Exit Sub
End Sub
</source>
EBEngine.Errors
<source lang="vb"> Public Sub ShowErrors() Dim db As Database Dim recT As Recordset Dim errE As Error On Error GoTo ShowErrors_Err Set db = CurrentDb() Set recT = db.OpenRecordset("NonExistantTable") recT.Close
ShowErrors_Exit:
Exit Sub
ShowErrors_Err:
Debug.Print "Err = " & Err.Number & ": " & Err.Description Debug.Print For Each errE In DBEngine.Errors Debug.Print "Errors: " & errE.Number & ": " & errE.Description Next Resume ShowErrors_Exit End Sub </source>
error handling by checking the Error code
<source lang="vb"> Public Sub ErrorHandling() On Error GoTo ErrorHandling_Err Dim dblResult As Double dblResult = 10 / InputBox("Enter a number:") MsgBox "The result is " & dblResult
ErrorHandling_Exit:
Exit Sub
ErrorHandling_Err:
Select Case Err.Number Case 13 " Type mismatch - empty entry Resume Case 11 " Division by 0 dblResult = 0 Resume Next Case Else MsgBox "Oops: " & Err.Description & " - " & Err.Number Resume ErrorHandling_Exit End Select End Sub </source>
Ignoring an Error and Continuing Execution
<source lang="vb">
Sub TestResumeNext()
On Error Resume Next Kill "AnyFile" If Err.number = 0 Then Else MsgBox "the Error Was: " & Err.Description End If
End Sub
</source>
Looking Up the Call Stack for a Previous Error Handler
<source lang="vb">
Sub Func1()
On Error GoTo Func1_Err Debug.Print "I am in Function 1" Call Func2 Debug.Print "I am back in Function 1" Exit Sub
Func1_Err:
msgBox "Error in Func1" Resume Next
End Sub
Sub Func2()
Debug.Print "I am in Func2" Call Func3 Debug.Print "I am still in Func2"
End Sub
Sub Func3()
Dim sngAnswer As Single Debug.Print "I am in Func3" sngAnswer = 5 / 0 Debug.Print "I am still in Func3"
End Sub
</source>
Placing a Resume Next Statement in Your Error Handler
<source lang="vb">
Sub TestResumeNextInError()
On Error GoTo TestResumeNextInError_Err Kill "AnyFile" If Err.number = 0 Then Else msgBox "We Didn"t Die, But the Error Was: " & Err.Description End If Exit Sub
TestResumeNextInError_Err:
Resume Next
End Sub
</source>
Read user choice when dealing with error
<source lang="vb">
Public Sub ErrorTrap1()
Dim Answer As Long, MyFile As String Dim Message As String, CurrentPath As String On Error GoTo errTrap CurrentPath = CurDir$ ChDrive "A" ChDrive CurrentPath ChDir CurrentPath MyFile = "A:Data.xls" Application.DisplayAlerts = False ActiveWorkbook.SaveAs Filename:=MyFile
TidyUp:
ChDrive CurrentPath ChDir CurrentPath
Exit Sub
errTrap:
Message = "Error No: = " & Err.Number & vbCr Message = Message & Err.Description & vbCr & vbCr Message = Message & "Please place a disk in the A: drive" & vbCr Message = Message & "and press OK" & vbCr & vbCr Message = Message & "Or press Cancel to abort File Save" Answer = MsgBox(Message, vbQuestion + vbOKCancel, "Error") If Answer = vbCancel Then Resume TidyUp Resume
End Sub
</source>
Using Resume Conditionally Based on User Feedback
<source lang="vb">
Function GoodResume()
On Error GoTo GoodResume_Err Dim strFile As String strFile = Dir(strFileName) If strFile = "" Then GoodResume = False Else GoodResume = True End If Exit Function
GoodResume_Err:
Dim intAnswer As Integer intAnswer = MsgBox(Error & ", Would You Like to Try Again?", vbYesNo) If intAnswer = vbYes Then Resume Else Exit Function End If
End Function
</source>
Using the Resume <LineLabel> Statement to Specify Where Execution Continues After an Error Occurs
<source lang="vb">
Sub TestResumeLineLabel()
On Error GoTo TestResumeLineLabel_Err Dim sngResult As Single sngResult = 1 / 0
TestResumeLineLabel_Exit:
Exit Sub
TestResumeLineLabel_Err:
msgBox "Error #" & Err.number & ": " & Err.Description Resume TestResumeLineLabel_Exit
End Sub
</source>
Активизирует подпрограмму обработки ошибок и указывает положение подпрограммы в процедуре; используется также для отключения подпрограммы обработки ошибок.
- On Error GoTo строка — Активизирует подпрограмму обработки ошибок, начало которой определяется обязательным аргументом строка, значением которого может быть любая метка строки или номер строки. Если возвращается ошибка выполнения, управление передается на указанную строку и запускается обработчик ошибок. Аргумент строка должен определять строку в той же процедуре, в которой находится инструкция On Error; в противном случае возникает ошибка компиляции.
- On Error Resume Next — Указывает, что возникновение ошибки выполнения приводит к передаче управления на инструкцию, непосредственно следующую за инструкцией, при выполнении которой возникла ошибка. Рекомендуется при доступе к объектам использовать эту форму инструкции, а не On Error GoTo.
- On Error GoTo 0 — Отключает любой активизированный обработчик ошибок в текущей процедуре.
Замечания
Если не выполнена инструкция On Error, то любая ошибка выполнения является фатальной; это означает, что выводится сообщение об ошибке и выполнение программы прекращается.
«Включенным» обработчиком ошибок называют подпрограмму, которая указана в инструкции On Error; «активным» обработчиком ошибок является включенный обработчик ошибок, который обрабатывает текущую ошибку. Если ошибка возникает в самом обработчике ошибок (в промежутке между возникновением ошибки и выполнением инструкции Resume, Exit Sub, Exit Function или Exit Property), то обработчик ошибок, определенный в текущей процедуре, не может обработать ошибку. Управление в этом случае возвращается в вызывающую процедуру; если в вызывающей процедуре включен обработчик ошибок, то обработка ошибки передается ему. Если этот обработчик ошибок является в данный момент активным, т.е. уже обрабатывает ошибку, то управление снова передается назад в вызывающую процедуру и т.д. до тех пор, пока не будет найден включенный, но не активный обработчик ошибок. Если включенный, но неактивный обработчик ошибок найден не будет, ошибка становится фатальной в том месте программы, в котором она впервые возникла. При каждой передаче управления обработчиком ошибок в вызывающую процедуру эта процедура становится текущей. После завершения обработки ошибки обработчиком в любой процедуре возобновляется выполнение текущей процедуры с той ее части, которая указана в инструкции Resume.
Подпрограмма обработки ошибок не может быть процедурой Sub или Function. Эта подпрограмма должна быть частью программы, которая отмечается с помощью метки строки или номера строки.
Для определения причины ошибки в подпрограммах обработки ошибок используют значение свойства Number объекта Err. Необходимо обеспечить в подпрограммах обработки ошибок проверку или сохранение существенных значений свойств объекта Err перед тем, как может возникнуть новая ошибка или перед вызовом процедуры, в которой может возникнуть новая ошибка. Значения свойств объекта Err описывают последнюю ошибку. Текст сообщения об ошибке, соответствующего коду ошибки Err.Number содержится в свойстве Err.Description.
Конструкция On Error Resume Next задает продолжение выполнения с инструкции, непосредственно следующей за инструкцией, которая привела к ошибке выполнения, или с инструкции, непосредственно следующей за вызывающей инструкцией в процедуре, содержащей конструкцию On Error Resume Next. Это позволяет продолжить исполнение программы несмотря на ошибку выполнения. Это позволяет также встроить подпрограмму обработки ошибок в процедуру, а не передавать управление в другую часть процедуры. Конструкция On Error Resume Next становится неактивной при вызове новой процедуры, поэтому для внутренней обработки ошибок необходимо выполнять инструкцию On Error Resume Next в каждой вызываемой процедуре.
При обработке ошибок, возникающих при доступе к другим объектам, рекомендуется использовать конструкцию On Error Resume Next, а не конструкцию On Error GoTo. Проверка объекта Err после каждого взаимодействия с другим объектом позволяет устранить неопределенность в том, при доступе к какому объекту возникла ошибка. Это позволяет всегда точно знать, какой объект поместил значение кода ошибки в свойство Err.Number, а также в каком объекте возникла ошибка (эта информация содержится в свойстве Err.Source).
Конструкция On Error GoTo 0 отключает обработку ошибок в текущей процедуре. Эта конструкция не задает переход на строку 0 для обработки ошибок, даже если в процедуре имеется строка с номером 0. Если инструкция On Error GoTo 0 не выполнялась, то обработчик автоматически отключается при выходе из процедуры.
Для того, чтобы предотвратить выполнение программы обработки ошибок в тех случаях, когда ошибка не возникла, следует помещать соответствующую инструкцию Exit Sub, Exit Function или Exit Property сразу после подпрограммы обработки ошибки, как в следующем примере:
Sub InitializeMatrix(Var1, Var2, Var3, Var4) On Error GoTo ОбработкаОшибок . . . Exit Sub ОбработкаОшибок: . . . Resume Next End Sub
В этом примере программа обработки ошибок помещена между инструкциями Exit Sub и End Sub, что позволяет отделить ее от части программы, соответствующей нормальному выполнению процедуры. Программу обработки ошибок можно разместить в любом месте процедуры.
Ошибки, которые не были перехвачены, возвращаются в управляющее приложение, если объект был запущен как исполняемый файл. В среде разработчика такие ошибки возвращаются в управляющее приложение только при указании соответствующих параметров. За описанием необходимых значений этих параметров и способов их задания следует обращаться к документации главного приложения. Там же следует проверить, позволяет ли главное приложение создавать классы.
Err.Number = vbObjectError + 1052
Системные ошибки при вызовах библиотек динамической компоновки (DLL) не приводят к возникновению исключений и не перехватываются средствами Visual Basic. При вызове функций из библиотек DLL необходимо проверять, успешно ли возвращается каждое значение (согласно спецификациям API), и в случае неудачи проверять значение свойства LastDLLError объекта Err.
Пример
В начале этой программы инструкция On Error GoTo определяет положение подпрограммы обработки ошибок в процедуре. В данном примере попытка удалить открытый файл приводит к возникновению ошибки с кодом 55. Ошибка обрабатывается в подпрограмме, после чего управление возвращается инструкции, которая привела к возникновению ошибки. Инструкция On Error GoTo 0 отключает перехват ошибок. После этого инструкция On Error Resume Next задает отложенный перехват ошибок, что позволяет точно определить, в каком контексте возникла ошибка, генерируемая в следующей инструкции. Следует отметить, что после обработки ошибки вызывается метод Err.Clear для сброса значений свойств объекта Err.
Sub OnErrorStatementDemo() On Error GoTo ErrorHandler ' Включаем программу обработки ' ошибок. Open "TESTFILE" For Output As #1 ' Открываем файл. Kill "TESTFILE" ' Попытка удалить открытый ' файл. On Error Goto 0 ' Отключаем перехват ошибок. On Error Resume Next ' Откладываем перехват ошибок. ObjectRef = GetObject("MyWord.Basic") ' Запускаем несуществующий ' объект, а затем проверяем ' ошибку механизма управления ' программируемыми объектами. If Err.Number = 440 Or Err.Number = 432 Then ' Выводим сообщение для пользователя и очищаем объект Err. Msg = "Ошибка при попытке открыть программируемый объект!" MsgBox Msg, , "Проверка отложенной ошибки" Err.Clear ' Очищаем поля объекта Err. End If Exit Sub ' Выходим из процедуры, чтобы ' не попасть в обработчик. ErrorHandler: ' Обработчик ошибок. Select Case Err.Number ' Определяем код ошибки. Case 55 ' "Ошибка "Файл уже открыт". Close #1 ' Закрываем открытый файл. Case Else ' Здесь размещаются инструкции для обработки других ошибок... End Select Resume ' Возобновляем выполнение ' со строки, вызвавшей ошибку. End Sub