Does "On Error GoTo errorHandler:" remain active for the rest of the sub if it is not triggered? - vba

This Code tries to select a checkbox; if it does not exist it will create the checkbox via the errorHandler. I was wondering; if an error occurs later in this sub, will it still be directed to that errorHandler? In other words, will VBA attempt to create a checkbox anytime I have an error during this sub? Or does ErrorHandling only look at the next line that occurs after "On Error Goto ...:"
Sub PrepNewPurchaseEntryForm()
'no relevant VBA happens here
Dim companyCount As Integer: companyCount = colCount("CompanyUserEntries")
Call FocusOnSheet("NewPurchaseEntry")
For i = 3 To companyCount
Dim companyName As String: companyName = Sheets("CompanyUserEntries").Cells(1, i).Value
On Error GoTo Err1:
ActiveSheet.Shapes.Range(Array(companyName)).Select
''if error occurs here, will Err1 still be called?
Next i
''if error occurs here, will Err1 still be called?
Exit Sub
Err1:
Call PlaceCheckBox(("B" & (i + 7)), companyName)
Resume Next
End Sub
Sorry this might seem like a stupid question but I have been reading up on ErrorHandling but i couldn't really find anything that explicitly explained this.

On Error GoTo xxxx error handler will stay active as long as you you don't set another error handler or remove it with On Error Goto 0

Related

Skip code if no error occurs by using an "if statement" in VBA

I need some input, since I'm quite new to VBA.
I have a part of a code that is looking for a file in three different path location. The code tries path 1, if error, it will continue to the next path (that's perfect)
My problem is, if the file path is working for code "test 2" and "test 3", It will always run until last one (i.e. "test 3") instead of jumping to next part of code. If the location path works for example for test 1 or test 2 location, I don't need to run the following lines after.
How can I make my code skip that part?
'Test 1 location path
On Error GoTo Err1:
sFldr = "Path1"
Err1:
Resume Next
'Test 2 location path
On Error GoTo Err2:
sFldr = "Path2"
Err2:
Resume Next
'Test 3 location path
On Error GoTo Err3:
sFldr = "Path3"
Err3:
Resume Next
'next part of big code
more code here
If I understand correctly, you need to progressively try values for (in this contrived example) sFldr, and the operation might error. Instead of using On Error Goto, just test the Err object for an error directly:
On Error Resume Next
sFldr = "Path1"
If Err.Number <> 0 Then
Err.Clear
sFldr = "Path2"
If Err.Number <> 0 Then
Err.Clear
sFldr = "Path3"
End If
End If
On Error GoTo 0 'Or resume your normal error handling.
'next part of big code
Call it like this:
If GetFldrValue = vbNullString Then
'whatever you need to do if all 3 fail.
End If
'next part of big code
Another option is to extract the whole thing into its own function (which might not be a bad idea - the comment "next part of big code" indicates that the routine might be doing too much). If you do that, you can just turn error handling off entirely and return the first valid value found:
Function GetFolderValue() As String
On Error Resume Next
GetFolderValue = "Path1"
If Err.Number <> 0 Then Exit Function
GetFolderValue = "Path2"
If Err.Number <> 0 Then Exit Function
GetFolderValue = "Path3"
End Function
One Procedure = One Error Handler.
Simple as that.
Make sure the error-handling subroutine only ever runs in an error state.
I can't really give you a more concrete answer than that, because the code you're showing us does literally nothing; assigning a string literal to a string variable is never going to raise an error, ...and a workflow that jumps around up and down from one line to another in the "normal" execution path isn't sane - you need to restructure things. I'd love to help, but I've no idea what your code does.
To put it shortly, you should have small procedures that look like this:
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
Option 1: Wrap in Function
It would be best to wrap this in a function that is responsible for retrieving the value of sFldr.
Option 2: GoTo Statement - not recommended
Perhaps add a GoTo if the value of sFldr is not null
'Test 1 location path
On Error GoTo Err1:
sFldr = "Path1"
If(sFldr <> "") Then Goto ContinueFunc
Err1:
Resume Next
'Test 2 location path
On Error GoTo Err2:
sFldr = "Path2"
If(sFldr <> "") Then Goto ContinueFunc

VBA - Application.EnableCancelKey is acting somehow strange

Pretty much the question is the following - why in the immediate window I get this:
Application.EnableCancelKey = 2
?Application.EnableCancelKey
1
The last line should be 2 or am I missing something?
Thanks!
Edit:
The basis is that the enum xlErrorHandler states that "The interrupt is sent to the running procedure as an error, trappable by an error handler set up with an On Error GoTo statement.", you need an error handler to use that enum (xlErrorHandler or 2).
Since some statements are not possible within the immediate window, and On Error GoTo is one of these statements, you cannot have an error handler in the immediate window and thus change the value of the EnableCancelKey to 2. Hence Excel switches it automaticaly to 1, giving you the value 1 when you ask it to display the value.
The only solution would be to use a sub.
Original reply:
Ok, I used the code in the documentation provided on the MSDN and edited a few things to test it with the following code.
To explain shortly I stopped the execution by pressing just once the "ESC" button and thus preventing the textbox to be exited. You can watch in the Immediate Window that the last EnableCancelKey has changed in value normaly.
Sub Test1()
Debug.Print " Before execution result : " & Application.EnableCancelKey
On Error GoTo handleCancel
Application.EnableCancelKey = 2 'xlErrorHandler
Debug.Print " Regular execution result : " & Application.EnableCancelKey
For x = 1 To 10000 ' Do something 1,000,000 times (long!)
Debug.Print "Test"
Next x
handleCancel:
If Err = 18 Then
Debug.Print "Aborted macro result : " & Application.EnableCancelKey
MsgBox "You cancelled"
End If
End Sub
Hope this helps, I got the result expected.
You have to write an Error Handler to get the 2 value, else the code cannot catch the error, that's why you get the 1 every time you were executing the macro.
Since the description of the enum xlErrorHandler states that "The interrupt is sent to the running procedure as an error, trappable by an error handler set up with an On Error GoTo statement.", you need an error handler to use that enum.
With this it should work:
Sub test2()
On Error GoTo theEnd
Application.EnableCancelKey = 2
Debug.Print Application.EnableCancelKey
theEnd:
End Sub

Excel VBA error handling not working for second error

In the below code Errorhandler correctly takes care of first error when I enter a workbook that is not open or any random string. But when I click on retry and again enter a random name I get "Subscript out of Range" error # Workbooks(workbookname). Activate.
Can anyone help me why it is happening and how can I make it work. I have tried a lot of things. But nothing is working. This code is part of a larger program.
Sub test()
Dim workbkname As String
On Error GoTo Errorhandler
Retry:
workbookname = InputBox("Enter workbook name:", _
"Name Enrty")
If StrPtr(workbookname) = 0 Then
MsgBox ("Aborting Program")
End
End If
Workbooks(workbookname).Activate
Exit Sub
Errorhandler:
Response = MsgBox("Workbook " & workbookname & " not found", vbRetryCancel)
If Response = 4 Then
GoTo Retry
End If
End Sub
The issue here is that the VBA Error Handler does not clear the error once given a directive like GoTo. As a result, the code thinks that it has encountered an error within your error handling routine and thus throws the error up the stack.
In order to clear the error, you must either call Resume with a place to resume (either Resume alone to run the erroneous code again, Resume Next to resume the line of code following the error, or Resume can be called followed by a label, as below:
Sub ErrTest
On Error Goto ErrHndl
For i = 0 to 5
a = 1/i
nextiLabel:
Next i
Exit Sub
ErrHndl:
Resume nextiLabel
End Sub
You can also use Err.Clear() to remove the error, but that is less recommended.
See here for more info: Error Handling in Excel

Error handling only works once

I have a very simple VBA code that should try to open a nonexistent file, send me to an error handler, then back to my code in an infinite loop (intentionally).
However, the compiler only catches the error the first time, then breaks on the second pass.
I have tried every combination of On Error statements to send it back on the second pass, but nothing seems to work.
Here is the code:
Sub TestError()
On Error GoTo errError
lblError:
On Error GoTo errError
'Code that should raise an error and send to errError
Excel.Application.Workbooks.Open ("lakdfjldkj")
Exit Sub
errError:
MsgBox "Code didn't break"
GoTo lblError
End Sub
Is there something I'm missing, or is this a bug?
Don't test this unless you are at peace with having to kill Excel, or add an extra condition for looping
Use Resume to... well... resume processing after the error handler.
Sub TestError()
On Error GoTo errError
'Code that should raise an error and send to errError
Excel.Application.Workbooks.Open "lakdfjldkj"
Exit Sub
errError:
MsgBox "Code didn't break"
Resume
End Sub
I have used code like this to access a certain worksheet, if it is not found then the error handler creates one with the correct name and then resumes the processing.
You need to clear the error message by using:
Err.Clear
Source: https://msdn.microsoft.com/en-us/library/hh2zczch(v=vs.90).aspx
Basically it still thinks you're handling the error, and hasn't reset the handling mechanism, so it doesn't re-trigger until you clear it.
(Resume also clears the Error as well, as you can note by the reference above).
I have tested, it is possible to type :
Resume label1 (line label)
Resume next
Resume 110 (line number)

Excel VBA: On Error Goto statement not working inside For-Loop

I'm trying to cycle through a table in excel. The first three columns of this table have text headings, the rest of them have dates as headings. I want to assign those dates, sequentially, to a Date-type variable, and then perform some operations based on the date
To do this I am using a foreach loop on myTable.ListColumns. Since the first three columns do not have date headers, I have tried to set the loop up so that, if there is an error assigning the header string to the date-type variable, the loop goes straight to the next column
This seems to work for the first column. However, when the second column's header is 'assigned' to the date-type variable, the macro encounters an error even though it is within an error-handling block
Dim myCol As ListColumn
For Each myCol In myTable.ListColumns
On Error GoTo NextCol
Dim myDate As Date
myDate = CDate(myCol.Name)
On Error GoTo 0
'MORE CODE HERE
NextCol:
On Error GoTo 0
Next myCol
To reiterate, the error is thrown on the second round of the loop, at the statement
myDate = CDate(myCol.Name)
Can anyone explain why the On Error statement stops working?
With the code as shown, you're actually still considered to be within the error handling routine when you strike the next statement.
That means that subsequent error handlers are not allowed until you resume from the current one.
A better architecture would be:
Dim myCol As ListColumn
For Each myCol In myTable.ListColumns
On Error GoTo ErrCol
Dim myDate As Date
myDate = CDate(myCol.Name)
On Error GoTo 0
' MORE CODE HERE '
NextCol:
Next myCol
Exit Sub ' or something '
ErrCol:
Resume NextCol
This clearly delineates error handling from regular code and ensures that the currently executing error handler finishes before you try to set up another handler.
This site has a good description of the problem:
Error Handling Blocks And On Error Goto
An error handling block, also called an error handler, is a section of code to which execution is tranferred via a On Error Goto <label>: statement. This code should be designed either to fix the problem and resume execution in the main code block or to terminate execution of the procedure. You can't use the On Error Goto <label>: statement merely skip over lines. For example, the following code will not work properly:
On Error GoTo Err1:
Debug.Print 1 / 0
' more code
Err1:
On Error GoTo Err2:
Debug.Print 1 / 0
' more code
Err2:
When the first error is raised, execution transfers to the line following Err1:. The error hander is still active when the second error occurs, and therefore the second error is not trapped by the On Error statement.
You need to add resume of some sorts in your error handling code to indicate the error handling is over. Otherwise, the first error handler is still active and you are never "resolved."
See http://www.cpearson.com/excel/errorhandling.htm (specifically the heading "Error Handling Blocks And On Error Goto" and following section)
Follow-up to paxdiablo's accepted answer. This is possible, allowing two error traps in the same sub, one after the other :
Public Sub test()
On Error GoTo Err1:
Debug.Print 1 / 0
' more code
Err1:
On Error GoTo -1 ' clears the active error handler
On Error GoTo Err2: ' .. so we can set up another
Debug.Print 1 / 0
' more code
Err2:
MsgBox "Got here safely"
End Sub
Using On Error GoTo -1 cancels the active error handler and allows another to be set up (and err.clear doesn't do this!). Whether this is a good idea or not is left as an exercise for the reader, but it works!
Clearing all property settings of the Err object is not the same as resetting the error handler.
Try this:
Sub TestErr()
Dim i As Integer
Dim x As Double
On Error GoTo NextLoop
For i = 1 To 2
10: x = i / 0
NextLoop:
If Err <> 0 Then
Err.Clear
Debug.Print "Cleared i=" & i
End If
Next
End Sub
You'll notice the just like the OP, it will catch the error properly when i =1 but it will fail on line 10 when i = 2, even though we used Err.Clear
Dim ws As worksheets
For Each myCol In myTable.ListColumns
On Error GoTo endbit
Dim myDate As Date
myDate = CDate(myCol.Name)
On Error GoTo 0
'MORE CODE HERE
endbit:
Resume NextCol
NextCol:
' Next myCol
Exit Sub ' or something '