Problem: My macro doesn’t do what I want. I have an excel file with multiple columns. What I want is the macro
- to look for specific headers (if they exist in the file), then
- selects the entire column and
- resize it as specified in the script. If the specified header doesn’t exist in the file, the code should move on the next one without giving any error.
The code below changes the «Problem Description» size from 50 to 6 although 6 is the size for «Corrective Action Required?» header (which is not applicable in this case as that header doesn’t exist and hence the resizing requirement of 6 s/b simply ignored).
But that didn’t happened. Instead, the size of previous condition (changing the column size of «Problem Description» to 50 ) did change to 6.
Should I use a different method to write this macro and avoid using OnErrorResumeNext?
Sub Resize_specific_columns_OnErrResNxt()
'
' finds specific columns based on changed header names and resize them
On Error Resume Next
Cells.Find(what:="data domain", After:=ActiveCell, LookIn:= _
xlValues, lookat:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext _
, MatchCase:=False, SearchFormat:=False).Activate
ActiveCell.EntireColumn.Select
Selection.ColumnWidth = 8
On Error Resume Next
Cells.Find(what:="eDIM#", After:=ActiveCell, LookIn:= _
xlValues, lookat:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext _
, MatchCase:=False, SearchFormat:=False).Activate
ActiveCell.EntireColumn.Select
Selection.ColumnWidth = 6
On Error Resume Next
Cells.Find(what:="Problem Description", After:=ActiveCell, LookIn:= _
xlValues, lookat:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext _
, MatchCase:=False, SearchFormat:=False).Activate
ActiveCell.EntireColumn.Select
Selection.ColumnWidth = 50
On Error Resume Next
Cells.Find(what:="Corrective Action Required?", After:=ActiveCell, LookIn:= _
xlValues, lookat:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext _
, MatchCase:=False, SearchFormat:=False).Activate
ActiveCell.EntireColumn.Select
Selection.ColumnWidth = 6
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
Обработка ошибок в VBA Excel с помощью оператора On Error. Синтаксис выражений с оператором On Error. Пример кода с простым обработчиком ошибок.
On Error – это оператор, который используется для отслеживания ошибок во время исполнения кода VBA. При возникновении ошибки On Error передает информацию о ней в объект Err и включает программу обработки ошибок, начинающуюся с указанной строки.
В первую очередь, обработчик ошибок нужен для пользователей файлов Excel с кодами VBA. Любая ошибка приводит к прекращению выполнения программы, открытию редактора VBA с непонятным для пользователя сообщением или даже к полному зависанию приложения.
Обработчик ошибок позволяет завершить выполнение программы при возникновении ошибки и вывести сообщение пользователю с ее описанием.
Синтаксис выражений с On Error
Включает алгоритм обнаружения ошибок и, в случае возникновения ошибки, передает управление операторам обработчика ошибок с указанной в выражении строки. Stroka – это метка, после которой расположены операторы обработчика ошибок.
Включает алгоритм обнаружения ошибок и, в случае возникновения ошибки, передает управление оператору, следующему за оператором, вызвавшем ошибку.
Отключает любой включенный обработчик ошибок в текущей процедуре.
Простой обработчик ошибок
Шаблон простейшего обработчика ошибок:
Sub Primer() On Error GoTo Stroka ‘Блок операторов процедуры Exit Sub Stroka: MsgBox «Произошла ошибка: « & Err.Description End Sub |
Оператор On Error GoTo
размещается в начале процедуры, метка и обработчик ошибок – в конце процедуры. Название метки можно сменить на другое, в том числе на кириллице.
Оператор Exit Sub
обеспечивает выход из процедуры, если блок операторов выполнен без ошибок. Для вывода описания ошибки используется свойство Description
объекта Err
.
Примеры обработки ошибок
Пример 1
Деление на ноль:
Sub Primer1() On Error GoTo Инструкция Dim a As Double a = 45 / 0 Exit Sub Instr: MsgBox «Произошла ошибка: « & Err.Description End Sub |
Результат выполнения кода VBA Excel с обработчиком ошибок:
Пример 2
Выход за границы диапазона:
Sub Primer2() On Error GoTo Instr Dim myRange As Range Set myRange = Range(«A1:D4»).Offset(—2) Exit Sub Instr: MsgBox «Произошла ошибка: « & Err.Description End Sub |
Результат выполнения кода VBA Excel с оператором On Error GoTo
:
Пример использования выражений On Error Resume Next
и On Error GoTo 0
смотрите в статье: Отбор уникальных значений с помощью Collection.
Error handling refers to the way runtime errors are handled. Error handling in VBA is done using the On Error statement and the Err object. Runtime errors can be generated by attempting to execute error-causing code or they can be raised explicitly using the Err.Raise method. There are a number of built-in types of runtime errors, and custom errors can be defined as well. Each type of runtime error has a unique number which can be used to determine at runtime which type of error has occurred and respond accordingly.
On Error
The On Error statement is used to control what happens when a runtime error occurs. The On Error statement sets or removes the current error handling scope. When a runtime error occurs in VBA the error trap is triggered and if an On Error statement has been set, VBA will respond according to which type of On Error statement was used.
Statement | Description |
---|---|
On Error Resume Next | Skips lines of code that cause errors. Use with caution. |
On Error GoTo Line | When an error occurs execution will jump to a specified label or line number. |
On Error GoTo 0 | Clears the current error and disables error handling. |
On Error GoTo -1 | Clears the current error and resets the error trap. |
Resume | When used after the On Error GoTo statement, Resume will continue execution from the line of code that caused the error. |
Resume Next | When used after the On Error GoTo statement, Resume Next will continue execution from the line of code directly after the line that caused the error. |
Resume Line | When used after the On Error GoTo statement, Resume Line will jump to a specified label or line number and continue execution. |
On Error Resume Next
Public Sub Example()
On Error Resume Next
Err.Raise 1004
Debug.Print "Error was skipped"
End Sub
On Error GoTo Line
Public Sub Example()
On Error GoTo HandleError
Err.Raise 1004
Exit Sub
HandleError:
Debug.Print "Error Handled"
End Sub
Note: It is not recommended to use line numbers.
Public Sub Example()
10
20 On Error GoTo 80
30
40 Err.Raise 1004
50
60 Exit Sub
70
80 Debug.Print "Error Handled"
90
End Sub
On Error GoTo…Resume
Public Sub Example()
Dim N As Long
N = 0
On Error GoTo HandleError
'Divide by zero error will be fixed by error handler
Debug.Print 1 / N
Debug.Print "Error Handled."
Exit Sub
HandleError:
If Err.Number = 11 Then
Debug.Print "Handling 'Division by zero' Error..."
N = 1
Resume
Else
Err.Raise Err.Number
End If
End Sub
On Error GoTo…Resume Next
Public Sub Example()
On Error GoTo HandleError
Err.Raise 1004
Debug.Print "Error Handled. Resuming Next..."
Exit Sub
HandleError:
Debug.Print "Handling Error..."
Resume Next
End Sub
On Error GoTo…Resume Line
Public Sub Example()
On Error GoTo HandleError
Err.Raise 1004
Continue:
Debug.Print "Resuming..."
Exit Sub
HandleError:
Debug.Print "Error Handled"
Resume Continue
End Sub
Note: It is not recommended to use line numbers.
Public Sub Example()
10
20 On Error GoTo 100
30
40 Err.Raise 1004
50
60 Debug.Print "Resuming..."
70
80 Exit Sub
90
100 Debug.Print "Error Handled"
110 Resume 60
120
End Sub
On Error GoTo 0
Public Sub Example()
On Error Resume Next
Err.Raise 1004 'Error will be skipped
On Error GoTo 0
Err.Raise 1004 'Error will be raised
End Sub
On Error GoTo -1
Public Sub Example()
On Error GoTo HandleError1
Err.Raise 1004
Exit Sub
HandleError1:
Debug.Print "HandleError1"
On Error GoTo -1
On Error GoTo HandleError2
Err.Raise 1004
Exit Sub
HandleError2:
Debug.Print "HandleError2"
Exit Sub
End Sub
The Err Object
The Err Object is used to access information about a runtime error that has occurred. When a runtime error occurs, the Err object’s properties are filled with information about the error. The Err object can also be used to raise errors explicitly.
Member | Description |
---|---|
Clear | Clears properties of the Err object. Does NOT reset error trap or clear error handling scope. |
Description | A text description of the error. |
HelpContext | The context ID for a topic in a help file. |
HelpFile | The path to help file. |
LastDllError | Returns a system error code produced by a call to a dynamic-link library (DLL). Read-only. Always returns zero on Mac. |
Number | The error number, 0 through 65535. |
Raise | Raises a specified runtime error. |
Source | The name of the object or application that originated the error. |
The Err Object is a Singleton
The Err object has a single default global instance and cannot be instantiated. The Err object can be accessed anywhere in VBA just by typing Err.
Public Sub Example()
Err.Raise 1004 'No need to instantiate Err object
End Sub
Public Sub Example()
Dim E As ErrObject
Set E = New ErrObject 'Causes error
End Sub
Properties
The Err object’s properties are filled with data when a runtime error occurs. The Number property is especially important because it can be used to dynamically respond to different types of errors. If no runtime error has occurred, the Number property will be 0. Therefore, to determine if an error has occurred in a particular line, the Number property can be checked to see if it is 0 or not.
Public Sub Example()
On Error Resume Next
Err.Raise 1004
If Err.Number <> 0 Then
With Err
Debug.Print "Number: " & .Number
Debug.Print "Description: " & .Description
Debug.Print "Source: " & .Source
Debug.Print "HelpFile: " & .HelpFile
Debug.Print "HelpContext: " & .HelpContext
Debug.Print "LastDllError: " & .LastDllError
End With
Err.Raise Err.Number
Else
Debug.Print "No Error"
End If
End Sub
Clearing The Err Object
The Err object can be reset in a few different ways. The Err object is reset when Err.Clear is called, a Resume or Resume Next statement is executed for the error, or another On Error statement is executed. Exiting an error-causing procedure does not reset the Err object.
Note: Exiting an error-causing procedure does not reset the Err object.
Public Sub Example()
Call ErrorCausingProcedure1
Debug.Print Err.Number 'Prints 1004
Call ErrorCausingProcedure2
Debug.Print Err.Number 'Prints 0
Call ErrorCausingProcedure3
Debug.Print Err.Number 'Prints 0
Call ErrorCausingProcedure4
Debug.Print Err.Number 'Prints 0
End Sub
Public Sub ErrorCausingProcedure1()
'Exiting procedure doe NOT reset the Err object
On Error Resume Next
Err.Raise 1004
End Sub
Public Sub ErrorCausingProcedure2()
'Calling Err.Clear resets the Err object
On Error Resume Next
Err.Raise 1004
Err.Clear
End Sub
Public Sub ErrorCausingProcedure3()
'Resume Next resets the Err object
On Error GoTo HandleError
Err.Raise 1004
Exit Sub
HandleError:
Resume Next
End Sub
Public Sub ErrorCausingProcedure4()
'On Error statement resets the Err object
On Error Resume Next
Err.Raise 1004
On Error GoTo 0
End Sub
Calling Err.Clear only clears the Err object. Err.Clear does not reset the error handling trap or end the error handling scope. On Error GoTo -1 will clear the Err object and reset error trapping, allowing another error to be raised. On Error GoTo 0 will clear the Err object and clear the error handling scope.
Public Sub Example()
On Error Resume Next
'Error is skipped
Err.Raise 1004
'Prints 1004
Debug.Print Err.Number
'Err object is reset
Err.Clear
'Prints 0
Debug.Print Err.Number
'Error is skipped because error handling scope was not reset
Err.Raise 1004
'Prints 1004
Debug.Print Err.Number
'Clears Err object and error handling scope
On Error GoTo 0
'Prints 0
Debug.Print Err.Number
'Raises a runtime error
Err.Raise 1004
End Sub
Error Trapping Options
Error Trapping options can be selected which can override error handling code in VBA. To change error trapping options navigate to Tools → Options → General in the Visual Basic Editor.
Option | Description |
---|---|
Break on All Errors | Will enter break mode when any error is encountered regardless of error handling code. |
Break in Class Module | Will enter break mode and show errors inside class modules. |
Break on Unhandled Errors | This is the default setting. Will enter break mode when an error is encountered and it is not handled by code. |
CVErr Function
The CVErr function can be used to return an error from a function. CVErr returns a value of type Variant with subtype Error. Only variables of type Variant can be assigned a value using the CVErr function. CVErr can take any error number as an argument. CVErr can be used to return a cell error from a user-defined function that is intended for use in spreadsheets.
Option Explicit
Public Function ReturnValueError() As Variant
'Shows #VALUE Error in cell
ReturnValueError = CVErr(xlErrValue)
End Function
xlErr Constant | Cell Error |
---|---|
xlErrBlocked | #BLOCKED! |
xlErrCalc | #CALC! |
xlErrConnect | #CONNECT! |
xlErrDiv0 | #DIV/0! |
xlErrField | #FIELD! |
xlErrGettingData | #GETTING_DATA |
xlErrNA | #N/A |
xlErrName | #NAME? |
xlErrNull | #NULL! |
xlErrNum | #NUM! |
xlErrRef | #REF! |
xlErrSpill | #SPILL! |
xlErrUnknown | #UNKNOWN! |
xlErrValue | #VALUE! |
IsError Function
The IsError function returns True if the argument expression evaluates to an error. IsError can be used to test if a cell value contains an error or a user-defined function returns an error. To return an error from a function use the CVErr function.
Option Explicit
Public Sub Example()
If IsError(Range("A1").Value) Then
Debug.Print "Range A1 contains an error."
End If
Dim E As Variant
E = ReturnError()
If IsError(E) Then
Debug.Print "E is Error: " & CStr(E)
End If
End Sub
Public Function ReturnError() As Variant
ReturnError = CVErr(xlErrValue)
End Function
Error Function
The Error/Error$ function is used to return the description text of an error. The Error function can be used to return a specific error description based on an error number or it can return the description text of the last error to occur.
Specific Error Description
Pass the optional ErrorNumber argument to the Error function to return a specific error description. Although the Error function can take a number between -2147483648 and 65535, it should be intended for use with the range 0 through 65535. If the error number is outside the valid range an Overflow runtime error will occur. If the error number is within the valid range but is not defined, the message «Application-defined or object-defined error» will be returned.
Public Sub Example()
Debug.Print Error(5) 'Prints: Invalid procedure call or argument
End Sub
Most Recent Error Description
Call the Error function with no ErrorNumber argument to return the description text for the last error to occur. If no error has occurred, a zero-length string will be returned. The Err.Description property can be used to get the text description of the most recent runtime error instead of using the Error function.
Public Sub Example()
On Error Resume Next
Err.Raise 5
Debug.Print Error() 'Prints: Invalid procedure call or argument
End Sub
Raising Errors
Use the Err.Raise method to raise a runtime error. Errors should be raised when an unacceptable state has been reached in a program. Existing VBA error numbers can be used to raise errors or custom error numbers can be created.
Raising Existing Errors
An appropriate VBA error number can be selected which describes the error.
Public Function RandomLong(MinValue As Long, MaxValue As Long) As Long
If MinValue > MaxValue Then
Err.Raise 5
End If
Randomize
RandomLong = Int((MaxValue - MinValue + 1) * Rnd + MinValue)
End Function
User-Defined Errors
To raise a user-defined error, create an error number by using the vbObjectError constant and adding a number between 513 and 65535. The range 0 through 512 is reserved for system errors. The vbObjectError constant has the value -2147221504 so user-defined errors will be negative. To derive the positive portion of a user-defined error simply subtract the vbObjectError constant from the error number.
Public Sub Example()
On Error GoTo HandleError
Err.Raise Number:=vbObjectError + 513, Description:="Custom Error"
Exit Sub
HandleError:
Debug.Print Err.Number - vbObjectError, Err.Description 'Prints: 513 Custom Error
Resume Next
End Sub
Error Statement
The Error statement raises a runtime error for a given error number. The Error statement is included for backward compatibility with older versions of VBA and Err.Raise should be used instead for new code.
Public Sub Example()
'Backward compatible
Error 5
'Use this for new code
Err.Raise 5
End Sub
Error Numbers
Runtime errors each have a number used to identify what type of error it is. Error numbers can be used with the Err.Raise method, the Error statement, and the Error function. When a runtime error occurs, the Err.Number property will be set to the number associated with the type of error. Any positive error number not listed in the table below returns «Application-defined or object-defined error».
Error Number | Error Text |
---|---|
3 | Return without GoSub |
5 | Invalid procedure call or argument |
6 | Overflow |
7 | Out of memory |
9 | Subscript out of range |
10 | This array is fixed or temporarily locked |
11 | Division by zero |
13 | Type mismatch |
14 | Out of string space |
16 | Expression too complex |
17 | Can’t perform requested operation |
18 | User interrupt occurred |
20 | Resume without error |
28 | Out of stack space |
35 | Sub or Function not defined |
47 | Too many DLL application clients |
48 | Error in loading DLL |
49 | Bad DLL calling convention |
51 | Internal error |
52 | Bad file name or number |
53 | File not found |
54 | Bad file mode |
55 | File already open |
57 | Device I/O error |
58 | File already exists |
59 | Bad record length |
61 | Disk full |
62 | Input past end of file |
63 | Bad record number |
67 | Too many files |
68 | Device unavailable |
70 | Permission denied |
71 | Disk not ready |
74 | Can’t rename with different drive |
75 | Path/File access error |
76 | Path not found |
91 | Object variable or With block variable not set |
92 | For loop not initialized |
93 | Invalid pattern string |
94 | Invalid use of Null |
96 | Unable to sink events of object because the object is already firing events to the maximum number of event receivers that it supports |
97 | Can not call friend function on object which is not an instance of defining class |
98 | A property or method call cannot include a reference to a private object, either as an argument or as a return value |
321 (1 — 2) | Invalid file format |
322 | Can’t create necessary temporary file |
325 | Invalid format in resource file |
380 (1 — 2) | Invalid property value |
381 | Invalid property array index |
382 | Set not supported at runtime |
383 | Set not supported (read-only property) |
385 | Need property array index |
387 | Set not permitted |
393 | Get not supported at runtime |
394 | Get not supported (write-only property) |
422 | Property not found |
423 | Property or method not found |
424 | Object required |
429 | ActiveX component can’t create object |
430 | Class does not support Automation or does not support expected interface |
432 | File name or class name not found during Automation operation |
438 | Object doesn’t support this property or method |
440 | Automation error |
442 | Connection to type library or object library for remote process has been lost. Press OK for dialog to remove reference. |
443 | Automation object does not have a default value |
445 | Object doesn’t support this action |
446 | Object doesn’t support named arguments |
447 | Object doesn’t support current locale setting |
448 | Named argument not found |
449 | Argument not optional |
450 | Wrong number of arguments or invalid property assignment |
451 | Property let procedure not defined and property get procedure did not return an object |
452 | Invalid ordinal |
453 | Specified DLL function not found |
454 | Code resource not found |
455 | Code resource lock error |
457 | This key is already associated with an element of this collection |
458 | Variable uses an Automation type not supported in Visual Basic |
459 | Object or class does not support the set of events |
460 (1 — 2) | Invalid clipboard format |
461 | Method or data member not found |
462 | The remote server machine does not exist or is unavailable |
463 | Class not registered on local machine |
481 (1 — 2) | Invalid picture |
482 (1 — 2) | Printer error |
735 | Can’t save file to TEMP |
744 | Search text not found |
746 | Replacements too long |
1004 | Application-defined or object-defined error |
31001 | Application-defined or object-defined error. *Out of memory |
31004 | Application-defined or object-defined error. *No object |
31018 | Application-defined or object-defined error. *Class is not set |
31027 | Application-defined or object-defined error. *Unable to activate object |
31032 | Application-defined or object-defined error. *Unable to create embedded object |
31036 | Application-defined or object-defined error. *Error saving to file |
31037 | Application-defined or object-defined error. *Error loading from file |
Поискав по рунету материал на тему обработки ошибок в VBA, не увидал на первых двух страницах результатов поиска чего-то, что мне понравилось. Может плохо смотрел, но решил написать на эту тему свою статью.
Простите, но — немного словоблудия
Ошибки в программе
Ошибки времени исполнения программы возникают, когда среда программирования не может выполнить то, что вы хотите. Таких ситуаций может быть много. Например:
-
Вы обращаетесь к объекту по имени, а объекта с таким именем в коллекции нет
-
Вы хотите выделить ячеку на одном листе, а этот лист в данный момент не является активным (типичнейшая ошибка новичков в Excel VBA)
-
Вы хотите удалить отфильтрованные автофильтром строки, а фильтр вообще не вернул записей и удалять нечего
-
Вы ссылаетесь на элемент массива, который находится за пределами его границ.
-
Вы пытаетесь присвоить переменной значение, которое оно не может хранить. Например, переменной типа Long нельзя присвоить строковую константу или переменной типа Integer присвоить знанчение превышающее число 32767.
На любую из этих и сотни других ситуаций среда выполнения реагирует стандартно — прерывает ход выполнения программы на том операторе, где возникла ошибка или, как ещё принято говорить, исключение. На экран выводится информация о возникшей ошибке и предлагаются стандартные варианты для продолжения работы:
-
Continue (продолжить) — этот пункт во время возникновения ошибки всегда не активен. Он активен, когда по ходу выполнения программы вы использовали оператор Stop. Кстати это очень полезный оператор для отладки программы.
-
End (завершить) — завершение исполнения программы
-
Debug (отладка) — переход в режим отладки, в котором можно посмотреть, на каком операторе возникла ошибка, что содержат переменные, можно даже перетащить жёлтую полоску, подсвечивающую текущий оператор, назад, и модифицировать знанчение переменных через окно Immediate window (впрочем это экзотика). В общем случае кнопка Debug позволяет посмотреть, где случилась ошибка и попытаться понять почему так случилось.
Если вы — автор программы, в которой случилась ошибка, то вы, должно быть, в начале будете рады увидеть подобное окно, ибо только так вы сможете отловить основные ошибки, скрытые в вашем коде. Однако, если эту ошибку видит пользователь, то для него это, мягко говоря, безрадостное и малопонятное зрелище. Ещё хуже, если за эту программу вам заплатили деньги. Поэтому в среде худо-бедно профессиональных программистов принято предусматривать обработку ошибок в своих программах.
Почему вообще в коде возникают ошибки?
-
Много ошибок во время написания кода возникает по невнимательности или не совсем адекватного понимания того, что делаешь. Таких ошибок, как правило, очень много, особенно у начинающих программистов, но эти ошибки довольно легко отловить и исправить, так как, пока вы их не исправите, ничего не работает. Ну, например, вы должны извлечь данные из 5-го столбца, а вы извлекаете из 6-го, а их там банально нет. Ясно, что вы это очень быстро заметите.
-
Вторая группа ошибок — это ошибки оптимиста. Когда программа написана в целом правильно, но алгоритм не готов к ударам судьбы в виде неожиданных действий со стороны пользователя, ошибок ввода-вывода (вы рассчитывали считать данные из файла, а файла с таким именем не оказалось, либо он заблокирован другим приложением), особенностей конфигурации компьютера (разные версии ОС или офиса, которые в некоторых мелочах отличаются).
-
Тонкие логические ошибки. Чем сложнее программа, тем больше шансов, что модель задачи в вашей голове, ваша программа и реальность не совсем согласованы между собой. Пока вы не достигните достаточного погружения в задачу вы такие ошибки не найдёте и не исправите. Порой на это уходит много времени. Но это характерно для сложных задач.
-
Ошибки на стыке вашего приложения и сервисов ОС, приводящие к неожиданным крахам приложения. Такого вообще возникать не должно, но как мы понимаем, и ОС и офис содержат ошибки, да и вы (что более вероятно) можете пользоваться системными вызовами не правильно. Подобные ошибки — сущий кошмар, особенно когда они проявляются лишь на некоторых конфигурациях, при определенных условиях, их трудно поймать и надёжно воспроизвести.
Задачи механизмов обработки ошибок
-
Обеспечить стабильную работу программы. Возникновение ошибки, появление которой вы не предусмотрели, приведёт в большинстве случаев к аварийному завершению всей программы или её части. При определенном уровне подобных ситуаций это ведёт к тому, что программой пользоваться становится невозможно.
-
Информирование. Мало обработать ошибку и предотвратить завершение программы. Надо ещё и адекватно проинформировать пользователя о причинах нестандартного поведения программы. Частно причиной ошибок в программе являются некорректные действия пользователя, поэтому важно сообщать ему о них.
-
Защита данных от повреждения. Программа обязана защищать от непреднамеренных повреждений результаты своей или пользовательской работы. Деструктивные действия должны быть снабжены соответствующими предупредительными диалоговыми окнами. Часто ошибка, не обработанная должным образом может повредить нужные данные.
Файл примера
Скачать
Код без обработки ошибок
Вот простой пример с потолка. Если вызвать Example_00, то она прекрасно отработает без ошибок и вернёт это:
В функцию GetCalories передаётся строка с блюдом, а она должна вернуть его калорийность, сверившись с таблицей в A1:B7.
Давайте поищем слабые места в этом коде. Первое, что должно прийти в голову — если мы ищем, то, что произойдёт, если мы не найдём? А произойдёт, конечно же, ошибка. Её инициирует метод Match.
Ещё одно слабое место этой подпрограммы: функция возвращает вещественный тип Double, и даже, если поиск оказался удачным, то в Cells(intRow, 2) может случайно находиться текстовая строка, а потому, когда вы числовому типу попытаетесь присвоить строковый тип, также произойдёт ошибка. И, если вы второй ошибки сможете избежать за счёт дополнительного оператора if с проверкой через IsNumber(), то избежать первой ошибки таким способом нельзя. Что же делать? А вот тут на сцену выходят операторы обработки ошибок.
Есть 2 подхода к обработке ошибок: автономный подход и выносной. Эти термины я придумал только что, чтобы проще было их обсуждать.
Автономный подход
Смысл автономного подхода в том, чтобы не выносить сор из избы. Если в подпрограмме возникла ошибка, то мы должны предположить, на каком месте она возникнет и поджидать её там с дубиной. С ошибкой, в этом случае, разбираются обычно в операторе, идущем сразу после потенциально опасного места. Давайте смотреть, как это может выглядеть:
Итак, что тут сделано:
-
Сразу после объявления функции GetCalories_v1 идёт оператор on error resume next, который в случае возникновения в каком-либо месте ошибки, предписывает VBA просто передавать управление на следующий оператор, идущий после ошибочного.
-
Мы объявили переменные. Необъявленные переменные получают тип Variant и значение по умолчанию Empty. Объявленные переменные числовых типов инициируются нулём, строковые — пустой строкой, то есть я наперёд знаю, что они содержат, а это хорошо для обработки ошибок.
-
На вызове метода WorksheetFunction.Match у нас возникает ошибка, так как искомого значения в таблице нет. А это, между прочим, был оператор присваивания ( = ). Прежде, чем левой части оператора присваивания (intRow) что-то будет присвоено, необходимо вычислить правую часть оператора присваивания (WorksheetFunction.Match…), а поскольку в процессе этого вычисления возникает ошибка, то переменная intRow остаётся такой, какой была! А, как я уже сказал, VBA автоматически её инициализирует нулём до начала исполнения подпрограммы. Получается, что, если в этом операторе возникнет ошибка, то в intRow будет ноль. Если ошибки во время поиска не возникнет, то ноля там не будет ни при каких раскладах, так как строки на листе нумеруются с единицы.
-
И вот этот ноль мы и контролируем, добавляя оператор If. Если intRow больше нуля, то WorksheetFunction.Match отработала штатно, а если нет — то работу подпрограммы надо прерывать, но об этом чуть позже.
-
Далее мы помним, что Cells(intRow, 2) может теоретически вернуть строковое значение, которое вызовет ошибку Type missmatch при присвоении переменной типа Double (GetCalories_v1), поэтому мы вставляем дополнительную проверку промежуточной переменной varTemp тому, что она числовая. И если это так, то присваиваем GetCalories_v1 значение из varTemp.
-
В случае возникновения любой ошибки внутри GetCalories_v1 она просто вернёт ноль. Почему ноль? Потому что переменная GetCalories_v1 тоже инициализируется нулём и об этом не надо заботиться, а в случае ошибки она останется в неприкосновенности.
-
Соответственно родительский код (в нашем случае его роль играет процедура Example_01) должен проверить, а не вернёт ли GetCalories_v1 ноль, и быть готовым к этой ситуации.
-
А вот теперь тонкий момент, который не все понимают. Почему я использовал промежуточные переменные intRow и varTemp? Вроде бы есть очевидный ответ — чтобы не вычислять значение выражений с Match и Cells 2 раза. Отчасти это, конечно, так. Но это, в данном случае, не главная причина. Главная причина в том, что такой код
вызовет неправильное поведение программы. Если у нас Match вызовет исключение, то VBA передаст управление на СЛЕДУЮЩИЙ оператор, а следующий оператор в данном случае это то, что идёт после Then — присваивание переменной varTemp значения. Таким образом наша проверка на наличие ошибки сработает с точностью до наоборот, передав управление в ту часть кода, которая должна быть защищена от ситуации, когда Match не нашла строку в таблице. Вот почему важно в операторе If не иметь ничего такого, что могло бы вызвать ошибку.
-
Как видите, в этом подходе мне зачастую даже нет необходимости проверять объект Err, чтобы понять, что произошла ошибка, так как я ориентируюсь на то, что промежуточные переменные остаются неинициализированными, что является показателем наличия ошибки.
Выносной подход
Данный метод основан на том, что, когда возникает ошибка, то VBA передаёт управление на специальный участок кода — обработчик ошибок, который обычно размещают в конце подпрограммы. Это может выглядеть так:
Обратите внимание, что:
-
Оператор on error теперь в случае ошибки предписывает передавать управление на метку ErrorHandler, которая объявлена в конце кода процедуры GetCalories_v2
-
В коде мы никак не заботимся о каких-либо проверках. Возникла ошибка? Иди на метку — там разберутся.
-
Если ошибки не случилось, то, чтобы программа не стала исполнять строчки, предназначенные для обработки ошибок, перед меткой ErrorHandler обычно ставят оператор Exit Sub или Exit Function (в зависимости от типа подпрограммы).
-
Принципиальный момент — наличие оператора On Error Resume Next сразу после метки ErrorHandler. Дело в том, что после того, как вы перешли на метку ErrorHandler, очень опасно иметь действующим оператор On Error GoTo ErrorHandler, так как, если у вас в обработчике ошибки случится любая ошибка, то управление будет передано опять на метку и, как нетрудно понять, образуется бесконечный цикл. Поэтому сразу после метки мы возможность возникновения цикла ликвидируем оператором On Error Resume Next.
Что лучше?
Какой метод лучше применять зависит от ваших предпочтений и конкретных ситуаций. Грамотную обработку ошибок можно сделать и так и эдак. Вот несколько соображений по преимуществам и недостакам данных подходов:
Автономный подход
Преимущества | Недостатки |
Есть возможность точно идентифицировать каждую конкретную проблему (если вы её предусмотрели), возникающую во время исполнения, что позволит вам дать самые точные инстркции пользователю для предотвращения появления исключения в будущем. | Достаточно трудоёмок, так как подразумевает наличие большого количества проверок в коде. Каждое потенциально опасное действие должно быть снабжено соответствующим оператором If, в котором контролируется значение переменной или код ошибки. |
Надо хорошо представлять себе ситуации, где могут возникнуть ошибки, в противном случае ряд ошибок вы просто не заметите на этапе отладки. | |
Необходимо больше кода, а также требуется опыт и фантазия. | |
Необходимо больше промежуточных переменных |
Выносной подход
Преимущества | Недостатки |
Ни одна ошибка не проскочит незамеченной. | Не смотря на то, что вы перехватите все ошибки, отреагировать на них правильно затруднительно, так как вы, по большому счёту, не знаете, на каком операторе произошла ошибка и почему. |
Удобнее организовывать централизованный сбор логов по ошибкам в приложении. Однозначно, фаворит для больших проектов. |
Кратко пробежимся по операторам, функциям и объектам VBA, которые предназначены для обработки ошибок времени исполнения программы.
Операторы
On Error { GoTo label | Resume Next | GoTo 0 }
Оператор on error управляет тем, на какой участок вашего кода будет передано управление в случае возникновения ошибки. Данный оператор можно вставить в любое место вашей программы или подпрограммы. Есть 3 варианта:
-
On error goto label — после того, как этот оператор выполнен, ошибка, возникшая на других операторах программы приведёт к переходу на метку label.
-
On error resume next — после такого оператора, VBA будет игнорировать возникшую ошибку и передавать управление на следующий оператор, стоящий за тем, в котором возникла ошибка.
-
On error goto 0 — это режим по-умолчанию. В случае возникновения ошибки данный режим приведёт к появлению на экране стандартного обработчик ошибок VBA с кнопками End и Debug.
Resume { label | Next | [0] }
Данный оператор возобновляет выполнение программы. Применяется в выносном методе обработки ошибок.
-
resume label— возобновление с метки label
-
resume next — возобновление со следующего оператора
-
resume или resume 0 — возобновление с оператора, вызвавшего ошибку. Это имеет смысл, если вы устранили ошибку в своём обработчике. На мой взгляд, на практике такое применяется крайне редко.
Goto label
Переход на метку. Может пригодиться, однако, использование меток в коде для чего-то большего, чем обработка ошибок, считается страшным моветоном.
Exit { Do | For | Function | Sub }
Досрочный выход из циклов (Do или For) и досрочный выход из подпрограмм (функции или процедуры). Могут пригодиться при обработке ошибок, но вообще это операторы и без того чрезвычайно полезны.
Объект Err
-
Err — глобальный объект (его не надо объявлять, а можно сразу пользоваться), который содержит информацию о последней ошибке, случившейся в вашей программе. Проверяя Err сразу после возникновения исключения или после ситуации, которая могла привести к исключению, вы можете понять, что имело место на самом деле.
-
Свойство Err.Number — содержит числовой код ошибки, по которому их различают в программе. Поскольку Number — свойство по умолчанию, то вы можете его опускать, то есть Err и Err.Number — это эквиваленты. Значение ноль говорит о том, что ошибки не произошло.
-
Err.Description — содержит англоязычное краткое описание ошибки
-
Err.Source — возвращает имя модуля, в котором возникла ошибка
-
Err.Clear — сбрасывает последнюю ошибку. Err сбрасывается также при выполнении оператором Resume, Exit (любого типа кроме Do и For) и On Error.
-
Err.Raise — искусственно вызывает исключение указанного в переданном параметре типа. Можно использовать для тестирования вашей подсистемы обработки ошибок.
P.S.
Лично я привык в своих программах использовать автономный подход и, возможно, поэтому я не совсем осознаю все преимущества выносного подхода. Буду рад прочесть в комментариях ваше мнение на этот счёт. Тема обработки ошибок данной статьёй, конечно, быть исчерпана не может, но она послужит вам хорошей стартовой точкой в этом важном деле.
Читайте также:
-
Работа с объектом Range
-
Работа с объектом Range (часть 2)
-
Sheet happens
-
Поиск границ текущей области
-
Массивы в VBA
-
Структуры данных и их эффективность
-
Автоматическое скрытие/показ столбцов и строк