This is a very fussy question: I am inserting an Error Handler in a For To loop in Excel VBA; I want the content of the loop indented, such that:
For i = 0 to n
On Error GoTo ErrorHandler:
'~~> code here
ErrorHandler:
'~~> code here
Resume NextLoop
NextLoop:
Next
However, Excel VBA automatically cancels indentation of Error Handlers:
For i = 0 to n
On Error GoTo ErrorHandler:
'~~> code here
ErrorHandler:
'~~> code here
Resume NextLoop
NextLoop:
Next
I have tried to unselect option "Auto Indent" in "Tools" > "Options" > "Editor" but this hasn't worked.
How can I avoid this?
Edit There was a careless mistake in the original code. Thank you user2426679 for giving me the opportunity to fix it.
Original post
Code that uses GoTo Label can be a nightmare to understand and get error free. The only situation I know off in which GoTo Label might be appropriate is a fatal error exit where there is no intention to return. Jumping out of an error and trying to jump back is very difficult to get right and very difficult to understand when you return to it in a few months.
Is there more than one statement in your code that can throw an error? If so, how does the error handler know which error it is handling?
I favour:
On Error Resume Next ' Suspend normal error handling
Statement that might throw an error
On Error GoTo 0 ' Restore normal error handling
If Err.Number <> 0 then
' Code to handle error
End If
The value of Err.Number and Err.Description will tell what the error is and allow to write specific error handling code.
Some argue that taking error handling out of the main code keeps the main code clean. There is some merit in this argument. If there are dozens of potential errors, the analysis can become complicated and make the normal path difficult to isolate. But, in my experience, this is very unusual. Perhaps, you have a list of files some of which might not open. There are lots of reasons why a file does open but your code can do nothing about them. All it can do is display Err.Description and move onto the next file.
New text and code
Apart from the mistake in the code there is nothing in my original post that I now consider incorrect. However, I do not think the original post was as complete as it should be.
If you search for “VBA Err.Number” you will find sites that list VBA’s error handling codes. Since these sites come and go, I will not recommend my favourite. However, I try to generate errors to see what happens. Consider this code:
Option Explicit
Sub DemoErrorHandling()
Dim ErrDesc As String
Dim ErrNum As Long
Dim FileNum As Long
Dim PathFile As Variant
FileNum = FreeFile
For Each PathFile In Array("", "X:", "C:\Program Files\Common Files\Intel\" & _
"WirelessCommon\libeay32.dll")
On Error Resume Next
Open PathFile For Input As FileNum
ErrNum = Err.Number
ErrDesc = Err.Description
Close FileNum
On Error GoTo 0
Debug.Print """" & PathFile & """ gives error:"
Debug.Print " A " & Err.Number & " " & Err.Description
Debug.Print " B " & ErrNum & " " & ErrDesc
Next
End Sub
Which, on my system, outputs:
"" gives error:
A 0
B 75 Path/File access error
"X:" gives error:
A 0
B 76 Path not found
"C:\Program Files\Common Files\Intel\WirelessCommon\libeay32.dll" gives error:
A 0
B 0
Note, as user2426679 pointed out, On Error GoTo 0has cleared Err.Number and Err.Description. Only by saving these values in variables are they available for testing. Note, attempting to open an empty file name and a non-existent disc give different errors.
My code demonstrates that you can loop trying different files until one opens successfully. You could keep asking the user for a file until one opened without an error.
Related
I am currently working on a larger program which may throw an error at some point. So I added an error handler for that case. The primary goal of which is to close the opened excel workbook wbk so that I don't get too many excel-applications running in the background. As I don't know, which part of the program may cause the error, I don't know the state of wbk and whether it is open or not. I tried adding a check for Nothing but wbk is Nothing seems to be false at that point. With the following code I will get The remote server machine does not exist or is unavailable (Error 462) when the error handler is called:
Error Handler
On Error Resume Next
resp = MsgBox(prompt:="Es ist ein Fehler aufgetreten! " & vbCrLf & vbCrLf & "Soll versucht werden weiter fortzufahren?" & _
vbCrLf & vbCrLf & "Fehlercode: " & str(Err.Number) & " entstanden durch " & Err.Source & Chr(13) & Err.Description, _
Buttons:=vbCritical + vbYesNo + vbDefaultButton1, _
title:="Unbekannter Fehler", _
HelpFile:=Err.HelpFile, _
Context:=Err.HelpContext)
If Not wbk Is Nothing Then
wbk.Close False
End If
If resp = vbNo Then Exit Sub
Resume Next
Afaik, I shouldn't even get an error because of the On Error Resume Next. Edit: It should since "The error hander is still active when the second error occurs, and therefore the second error is not trapped by the On Error statement." - source
TL;DR: wbk should be closed in error handling if it is open, but it throws an error.
Edit:
I force an error in order to call the error handler. Said error does only happen in the second execution so the wbk value is set normally, then the workbook is closed and the program is called again and the error is forced before wbk is initialized again.
During the second execution, wbk has whatever value it has after wbk.Close was called, this is not Nothing. I did now explicitly set Set wbk = Nothing after closing and the error does no longer happen, due to the Nothing check above.
Btw. I changed the responses around since it doesn't really make sense to close the workbook when the code is resumed.
Following on from my Connections query, I now want to capture any connections that cannot be reached and throw an error message to state that the relevant connection cannot be reached.
This is the code I have so far, however, I'm not sure if it's capturing any errors:
Private Sub btnRefreshConns_Click()
On Error GoTo ErrorHandler
Dim cn As WorkbookConnection
For Each cn In ActiveWorkbook.Connections
cn.Refresh
Next
Exit Sub
ErrorHandler:
MsgBox "A connection could not be reached" & cn.Name & ": " & cn.Description
End Sub
Could someone please assist me or let me know if this code would work?
Yes, the sample will catch connection errors. Change the MsgBox line to this to fix the reporting:
MsgBox "A connection could not be reached: " & Err.Number & ": " & Err.Description
Err has info about the latest error.
There's only one catch: all errors will be caught, not just connection errors. That is why I suggest not referring to cn in your error handler: you don't know what state it will be in if an unrelated error happens (say, out of memory - which can happen!). A better approach is to wrap just the code of interest. Inside your For loop:
On Error GoTo ErrorHandler
cn.Refresh
On Error GoTo 0 ' Reset default error handling
Edit: The On Error GoTo 0 unhooks your ErrorHandler and returns to the default error-handling mode, which is to show the error dialog box. The 0 is just a magic number that VBA is programmed to recognize for this purpose. See more details and explanation at Chip Pearson's page on the subject, which is my personal go-to reference.
I have never used On Error GoTo -1, but this answer suggests I'm not missing anything :) . It is apparently used to continue executing normal code after ErrorHandler, but I have always used Resume for that and had no problems.
Caveat: never never never forget the Exit Sub before ErrorHandler. I did once and got stuck in an infinite error loop I could only escape by killing Excel. Resume causes an error if you run it when an error isn't actually being handled and that error threw to the error handler having the Resume... yeah. Ugly.
I have written an excel VBA script which refers to another open excel document for some of it's data. Recently it has come to my attention that if this secondary document is closed unexpectedly by the user, the primary script ceases to work. Obviously, I need to check to be sure it is open before I search it.
Below is the code I came up with to verify that the workbook is open. If it is, I format it. If it isn't, I open it (which triggers it's own formatting). The problem comes in because my error handler catches the "Object required" error number 424. I try to take care of that by instructing it to just resume next when this happens. Unfortunately it seems to want to pick case else rather than case 424 and stops the script.
On Error GoTo searchGridsError
GridName = Workbooks(SALTname).Sheets(2).Range("B3").value
If Verify.FirstOption.value = True Then
Set Verify.groupGrid = Workbooks(GridName)
If Verify.groupGrid Is Nothing Then
Verify.checkForGrids
Else
formatWorkbook
End If
End If
Below is my error handler:
searchGridsError:
Select Case Err
Case 18
Verify.clearData
Exit Function
Case 424
Resume Next
Case Else
MsgBox "An error has occurred while searching the customer number grid. Please try again or search manually."
Module1.ReportError Err.Number, Erl, Err.Description, "searchGrids", Verify.Address1Box & "," & Verify.Address2Box & "," & Verify.CityBox & "," & Verify.StateBox & "," & Verify.ZipBox & "," & Verify.ContractBox & "," & Verify.PBPBox & "," & Verify.CountyBox
Verify.clearData
Exit Function
End Select
End Function
Does anyone have any ideas about why this is happening? It has to be in the error handler but I have seen many, many examples that look just like mine.
As guitarthrower stated in the comments, simply putting Resume Next in your error handling does not resume your macro back where the error occurred. To do that you would need to put another placeholder after your On Error GoTo searchGridsError line like RestartHere: where you want to jump back to and then replace Resume Next with:
On Error Resume Next
GoTo RestartHere
However, this will bypass your error handling once Error 424 is encountered, so you should be wary of how it is used.
Probably a better solution would be to put your error handling right in your code where you expect the error to occur. You can leave your code mostly as-is. However, right before the line that is throwing Error 424, you add On Error Resume Next. Then after the line in question, you add the following:
If Err.Number = 424 Then
Err.Clear
End If
On Error GoTo searchGridsError
I'm encountering an odd VBA subscript out of range error in this simple sub :
Sub writeTypes(ByVal rowNb As Long, ByVal colNb, ws As Worksheet)
On Error GoTo ErrorHandler_ObjMethod
Const METHOD_NAME = "writeTypes (CCase)"
With ws
If Not isArrayEmpty(pTypes) Then
For i = LBound(pTypes) To UBound(pTypes)
If pTypes(i) <> "" Then
.Cells(rowNb, colNb).Value = .Cells(rowNb, colNb).Value & pTypes(i) & ";"
ElseIf i = UBound(pTypes) Then
.Cells(rowNb, colNb).Value = Left(.Cells(rowNb, colNb).Value, Len(.Cells(rowNb, colNb).Value) - 1)
End If
Next i
Else: .Cells(rowNb, colNb).Value = "N/A"
End If
End With
ErrorHandler_ObjMethod:
If err.Number <> 0 Then
Workbooks(AA_RESOURCES_WB).Close SaveChanges:=True
MsgBox (METHOD_NAME & vbNewLine & err.Number & vbNewLine & err.description)
End
End If
End Sub
The calling line of this procedure is : pUnassignedCases(i).writeTypes j, 7, ws
(The variables passed as params are correct I made sure of that several times)
Here is what I already tried to do :
-To remove the "ByVal"s in the parameters
-To remove the first "If Not"
-To remove the "Elseif" block
The removals were done properly without any syntax/logic errors.
I also checked any used variables (including the array of string that is "pTypes") in any way possible. All things seem perfectly fine.
I also tried to incorporate this code directly into my other sub (that goes through an array of CCase objects with a For loop) rather than calling it through a CCase object procedure, and somehow it works for the firsts CCases objects and then forces the loop to go beyond the CCase array upper bound... I can't make any sense of this.
When I go through the code line by line, the error occurs at the "End sub" line. But when I remove the error handling it goes fine but somehow the error gets passed on somewhere else in the code that isn't in anyway related to this sub and was working perfectly before... Then if I simply remove any calling of this sub everything is working as it already was.
Plus even when the error occurs, my worksheet line is still well updated with "N/A" (as it's supposed too because none of my cases objects have types for now). It's like the sub is cursed. This is driving me crazy.
A few things I noticed:
I don't know if these will fix the issue you are experiencing but they may be of use:
Parameter ByVal colNb has no type.
I suspect the line ElseIf i = UBound(pTypes) Then is intended to strip off the trailing ";"
If this is the case, similar code, might be best placed outside the for loop.
Consider: Can the array pTypes have a value that is <> "" in the UBound index position. If so you may have a logic error.
If the cell has no value and Len(.Cells(rowNb, colNb).Value) - 1) is used it will raise an error when used as an argument to the left function. I think this will occur when the array contains only empty strings.
isArrayEmpty: I'm wondering what this function does. I assume it does what it says on the tin.
The above don't explain your issue, "err" being lower case is weird as stated in another answer. If Exit sub is before the error handler it would stop the if statement being evaluated when no error has occurred.
Harvey
Code demonstrating poor error handling.
Sub a3(optional RaiseAnError As Boolean = true)
On Error GoTo errhand
If RaiseAnError Then
Err.Raise 1, "", "Simulating code that might raise and error "
End If
MsgBox "Got to the end of your code"
errhand:
If Err.Number <> 0 Then
MsgBox "Err.Number = " & Err.Number
Err.Raise 2, "", "Simulating code ie workbook close that might raise and error "
MsgBox "ok"
End
End If
End Sub
I have a question about the correct way to handle errors in VBA in Excel. If a specific error, say xxxxxxx, occurs, then a MsgBox should be displayed. If another error occurs the standard run-time error handler should pop up. How can this be accomplished? Here is the example code:
On Error Resume Next
'Line of code that causes an error here.
If Err.Number = xxxxxxx Then
MsgBox "Specific error message."
ElseIf Err.Number = 0 Then
Do nothing
Else 'Some error other than xxxxxxx.
'This is the problem. Here I would like to display standard run-time error
'handler without causing the error again.
End If
On Error GoTo 0
You can get a message box that looks very much like the standard error message by putting this into your "Else" block:
MsgBox "Run-time error '" & Err.Number & "':" & _
vbNewLine & vbNewLine & _
Error(Err.Number), vbExclamation + vbOKOnly, _
"YourProjectNameHere"
But this is just a facsimile. It's not the actual error message dialog that VB6 puts up; it's just formatted to look like it. Error handling is still disabled by the "On Error Resume Next" statement at this point.
But if you really, really want to invoke the standard error handling code, you can put this in the "Else" block:
Dim SaveError As Long
SaveError = Err.Number
On Error Goto 0
Error (SaveError)
This code saves the error number, re-enables error handling, and then re-raises the error. You invoke the VB runtime's true error handling machinery this way. But beware: if this error isn't caught with an active error handler somewhere higher in the call chain, it will terminate your program after the user clicks on the "OK" button.
Note that you'll also lose the ability to get the actual line number where the error occurs using" Erl" in that error handler because you are re-generating the runtime error with the "Error (SaveError)" statement. But that probably won't matter because most VB code doesn't actually use any line numbers, so Erl just returns 0 anyway.
Replace On Error Resume Next with
On Error Goto SomePlaceInCodeToHandleErrors
SomePlaceInCodeToHandleErrors:
If Err.Number = XXXX Then
MSGBOX "Message"
End If
Check out this Stack Overflow thread for some more information and example code.
In your VBA options, select "Break on unhandled errors".
To enable handling use on error goto SomeLabel or on error resume next.
To stop error handling use on error goto 0.
Your question is contradictory in this context. If you enable error handling, well, you disable the standard error handling.
As DaMartyr suggests, you can still use something like msgbox err.description.
So to follow up on JeffK's new suggestion the code below seems to work fine with VBA, and what's more I can't see the danger in using it. To terminate Excel is critical as that could lose a lot of work, but since the code always checks that the error is there how could that happen?
Thank you JeffK for this intriguing idea.
Dim savedNumber As Long
On Error Resume Next
'Line of code that causes an error.
If Err.Number = XXXXXXX Then
'Specific error message.
ElseIf Err.Number <> 0 Then
savedNumber = Err.Number
On Error GoTo 0
Error savedNumber
End If
Err.Clear
On Error GoTo 0