Putting Labels and Error Handler in the right place - vba

I am using an error handler in Vba and want to use in Error Handler in my code as suggested by many experts over here.
Sub test()
For i =1 to 100
On Error GoTo ErrHand:
Filename=Dir()
Folder=
Workbooks.open(Folder & Filename)
Label1:
Code
Code
Close the file
ErrHand:
Application.GetOpenFilename()
GoTo Label1
Next i
End Sub
I am finding difficulties in running the code in a normal way. I try to pen a file and if it fails, I call Error Handler and throw a prompt to select a file and then I close the file and do the same thing for next files. One difficulty I am facing is that the opened file never closes. Thanks in advance.

In addition to Jeeped's excellent suggestion:
Sub test()
On Error GoTo ErrHand:
For i =1 to 100
Filename=Dir()
set Folder= Workbooks.open(Folder & Filename)
'Label1:
Code
Code
folder.Close 'based on using 'folder', above
next
exit sub 'if you don't do this, code execution will go right into your error handler
ErrHand:
if err.Number = <something>
Application.GetOpenFilename()
'GoTo Label1
Resume Next 'this is a simpler version, and doesn't require a label and another GoTo
'skips the line of code that errored, continues with the next line
elseif err.number = <something else>
'...
Resume 'allows you to retry the same statement after making some changes
endif
'Next i
End Sub

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

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

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

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

Is there a way I can avoid using GoTo in this situation?

So I've got a sub (triggered by a command button) which runs a process that seems quite time-consuming (between 5 and 20 seconds, dependant on the machine and how co-operative our network is feeling). To make it clear to the user that stuff is happening that they can't see I change the mouse pointer to an hourglass then change it back when the sub exits, regardless of the reason for the exit.
With that in mind my code looks something like this (illustrative example, not actual code):
Private Sub cmdDoTheThing_Click()
On Error GoTo Err_cmdDoTheThing
Screen.MousePointer = 11 'Hourglass/Equivalent
'Check all data is available to Do The Thing
If Not MyModule.ThingIsDoable(Me.PrimaryKey) Then
MsgBox "Cannot Do The Thing, more preliminary things must be done first."
GoTo Exit_cmdDoTheThing
End If
'Try to Do The Thing (function returns false on failure)
If Not MyModule.DoTheThing(Me.PrimaryKey) Then
MsgBox "Processing of The Thing failed."
GoTo Exit_cmdDoTheThing
End If
'...
'Stuff here I don't want to do if either of the above failed
'...
Exit_cmdDoTheThing:
Screen.MousePointer = 0 'Default mouse pointer
Exit Sub
Err_cmdDoTheThing:
MsgBox "Error " & Err.Number & ": " & Err.Description
Resume Exit_DoTheThing
End Sub
I don't want to repeat Screen.MousePointer = 0 at every possible exit point so I figured a GoTo would serve as a decent shortcut since the Exit_cmdDoTheThing label was needed for error handling anyway.
Is this a valid use-case for a GoTo statement and if not is there some other way I can achieve the same result? I don't want a sudden raptor attack after all.
GoTo can be replaced by using a do-while block (which has a false condition and runs only once) and using 'Exit Do' wherever you want to skip the rest of the code.
So your code might now look like:
Private Sub cmdDoTheThing_Click()
On Error GoTo Err_cmdDoTheThing
Do
Screen.MousePointer = 11 'Hourglass/Equivalent
'Check all data is available to Do The Thing
If Not MyModule.ThingIsDoable(Me.PrimaryKey) Then
MsgBox "Cannot Do The Thing, more preliminary things must be done first."
Exit Do
End If
'Try to Do The Thing (function returns false on failure)
If Not MyModule.DoTheThing(Me.PrimaryKey) Then
MsgBox "Processing of The Thing failed."
Exit Do
End If
'...
'Stuff here I don't want to do if either of the above failed
'...
Loop While FALSE
Exit_cmdDoTheThing:
Screen.MousePointer = 0 'Default mouse pointer
Exit Sub
Err_cmdDoTheThing:
MsgBox "Error " & Err.Number & ": " & Err.Description
Resume Exit_DoTheThing
End Sub
GoTo has to be used with real caution as it may make the code really complex after some iterations in the code. GoTo also allows you to do very weird/ugly things such as jump out of scope without actually going out of scope. With the do-while you ensure the flow of the code while maintaining the sanity and readability of the code.
Raising a custom error can avoid the use of GoTo for subroutines with this error handling structure. This has the added benefit of making it clear to anyone reading the code that failure of certain functions to complete is considered an error in this situation, even if they do not raise an error upon failure.
Public Const cCustomErrNum = 9114
Private Sub cmdDoTheThing_Click()
On Error GoTo Err_cmdDoTheThing
Screen.MousePointer = 11 'Hourglass/Equivalent
'Check all data is available to Do The Thing
If Not MyModule.ThingIsDoable(Me.PrimaryKey) Then
Err.Raise cCustomErrNum,"cmd_DoTheThing_Click()", _
"Cannot Do The Thing, more preliminary things must be done first."
End If
'Try to Do The Thing (function returns false on failure)
If Not MyModule.DoTheThing(Me.PrimaryKey) Then
Err.Raise cCustomErrNum,"cmd_DoTheThing_Click()", _
"Processing of The Thing failed."
End If
'...
'Stuff here I don't want to do if either of the above failed
'...
Exit_cmdDoTheThing:
Screen.MousePointer = 0 'Default mouse pointer
Exit Sub
Err_cmdDoTheThing:
MsgBox "Error " & Err.Number & ": " & Err.Description
Resume Exit_DoTheThing
End Sub

Why VBA goes to error handling code when there is no error?

I have writen some code in VBA (Excel) with error handling labels. It worked fine until I recently notice the error handling code gets executed everytime, not just when an error occurs. Does anybody know why this happens? Thanks.
Here's a trivial test case where both msgboxes would pop up.
Sub example()
On Error GoTo err_handle
MsgBox "OK!"
err_handle:
MsgBox "not OK"
End Sub
You want to add an Exit Sub to your routine:
Sub example()
On Error GoTo err_handle
MsgBox "OK!"
Exit Sub
err_handle:
MsgBox "not OK"
End Sub
Look here for a full explaination.
It's because you aren't returning out of the subroutine after the first message box the (OK) one. After that is shown the next line of code is executed which is the "not ok" message.
You could either exit the subroutine early before your error handler label (ExitSub) or goto the end of the subroutine on success (bypassing the "error" routine)
need to add exit sub else the program continues to execute the label as it is the part of the code
Just as a clarification to add some meat to the other answers.
"err_handle:" is only error handling code because you are using it as such. It isn't intrinsically an error handler like a catch block in other languages.
Technically "err_handle:" is just a label that facilitates a goto jump. In your case the goto just happens to be used with an error handler (on error goto)
The generally accepted pattern for error handling is to have an error handler and an exit procedure. A pretty standard code stump might look like this:
Public Function Example() As String
Dim strRtnVal As String 'Return value variable.
On Error GoTo Err_Hnd
'***************************************************************************
'Lock interface code here (hourglass, screenupdating etc.)
'***************************************************************************
'***************************************************************************
'Your code here.
'***************************************************************************
Exit_Proc:
'Prevents "Error Loops" caused by errors within the exit procedure:
On Error Resume Next
'***************************************************************************
'Restore Interface.
'***************************************************************************
Example = strRtnVal 'Set Return Value.
Exit Function
Err_Hnd:
'Display message, do some logging, whatever.
Resume Exit_Proc '<- Run exit procedure.
End Function