Error Handling in VBA for Word - vba

Even though I clearly have the error blocked off
For Each sentence In ActiveDocument.Paragraphs
j = j + 1
On Error GoTo hey:
third_word(j) = sentence.Range.Words(3)
sw(j) = sentence.Range.Words(2)
tot_sent(j) = sentence.Range.Text
hey:
Next
The code is still throwing error 5941: the requested member collection does not exist. That is exactly the type of error that I'm trying to except and make the code continue. A lot of the paragraphs do not have a third word so I only want to put a sentence into the tot_sent array if the sentence has a third word. The best way to do this is through error handling but it is not working.

Try this:
For Each sentence In ActiveDocument.Paragraphs
j = j + 1
If sentence.Range.Words.Count > 2 Then
third_word(j) = sentence.Range.Words(3)
sw(j) = sentence.Range.Words(2)
tot_sent(j) = sentence.Range.Text
End If
Next

It is better to check for conditions that may cause errors, but if you really want to use On Error then you need to Resume once an error has occurred. If you don't Resume after processing the error, then you are still in "error handling" mode and any subsequent error cannot be trapped.
For Each sentence In ActiveDocument.Paragraphs
j = j + 1
On Error GoTo hey:
third_word(j) = sentence.Range.Words(3)
sw(j) = sentence.Range.Words(2)
tot_sent(j) = sentence.Range.Text
ResumePoint:
Next
'...
'...
Exit Sub
hey:
' perform whatever processing is required for the error
'...
Resume ResumePoint
End Sub

Related

Handling Recurring VBA Errors Within Subroutine

I am stuck on something that I have a workaround for, but it bugs me I don't have a direct answer for how to address the issue of using On Error Goto for recurring errors. My question is essentially the same as this one, however the answers provided are alternatives to the OP's overall approach rather than how to handle the specific issue.
I've simplified an example below. I am WELL AWARE that there are probably a dozen ways this code could be rewritten to avoid any error -- I am just using this to illustrate the issue of trying to figure out why/how does this code work properly when i = 0, but not when i = 3,6,9?
Sub vbaErrorConfusion()
Dim theArray(9) As Long, i As Long
For i = 0 To 9
On Error GoTo fixingErrors
'next line will intentionally create an error when
'when i = {0} this works as expected, but at i=3 next line throws an error.
theArray(i) = i + 1 / (i Mod 3)
On Error GoTo 0
next_i:
Next i
Exit Sub
'----Error Handling----
'this works as expected when i=0 but not when i = 3,6,9
fixingErrors:
Err.Clear
On Error GoTo 0
'at this point I would expect my error state to be the same as the start of procedure?
theArray(i) = -1
GoTo next_i
End Sub
What my Err variable shows after first instance of 0.
After I run Err.Clear I would expect the behavior for i=3 to be the same as when i=0, however my procedure stops with the below VBA error one would expect WITHOUT any error catching.
I presume there's some way to reset the Error variable or handle this type of situation without a workaround? Any quality responses will get upvoted. Thanks.
To tell VBA that an you have dealt with the error you need a Resume statement. Therefore, replacing the line GoTo next_i with Resume next_i you give you the outcome you expect.
You do not need Err.Clear. Also, On Error GoTo 0 will disable the error handler. However, neither of these lines will tell VBA that you have dealt with an error.
In your code at i=0 the error handler is activated but VBA is not told that the error has been dealt with (i.e. no Resume statement). At i=3 another error occurs while a previous error hadn't been dealt with. In this case the current procedure/function/property cannot deal with the second error, which is therefore, fatal.
You should also take the On Error GoTo fixingErrors line outside the loop.
Sub vbaErrorConfusion()
On Error GoTo fixingErrors
Dim theArray(9) As Long, i As Long
For i = 0 To 9
'*On Error GoTo fixingErrors
'next line will intentionally create an error when
'when i = {0} this works as expected, but at i=3 next line throws an error.
theArray(i) = i + 1 / (i Mod 3)
'*On Error GoTo 0
next_i:
Next i
Exit Sub
'----Error Handling----
'this works as expected when i=0 but not when i = 3,6,9
fixingErrors:
'*Err.Clear
'*On Error GoTo 0
'at this point I would expect my error state to be the same as the start of procedure?
theArray(i) = -1
Resume next_i
End Sub
Please take some time to read up on how Error handling in VBA works.
Sub vbaErrorConfusion()
Dim theArray(9) As Long, i As Long
For i = 0 To 9
On Error GoTo fixingErrors
'next line will intentionally create an error when
'when i = {0} this works as expected, but at i=3 next line throws an error.
theArray(i) = i + 1 / (i Mod 3)
'On Error GoTo 0
'next_i:
Next i
Exit Sub
'----Error Handling----
'this works as expected when i=0 but not when i = 3,6,9
fixingErrors:
Err.Clear
Debug.Print "An error was generated for i= ", i
'On Error GoTo 0
'at this point I would expect my error state to be the same as the start of procedure?
theArray(i) = -1
Resume Next
End Sub

Resume to a label not working as expected

I am having an issue using Resume and although I have found another solution by using an On Error GoTo, I am still confused as to why the code below doesn't work. The initial error occurs because the sheet name "Sheet_1" is already taken. This means that, in the watch window, err.number has a value of 1004 just before the Resume NameAgain is executed. Rather than clear the error and jump back up to the label, an error 20 occurs(resume without error), and the code moves to the End If line.
Given that there is an active error 1004, I can't understand why it acts as though there isn't an error. I have searched the site for Error 20 issues but nothing really resolved this for me or made me understand the logic behind it. Any help is much appreciated.
Sub ErrorTest()
Dim i as integer:i=1
NameAgain: On Error Resume Next
Worksheets("Main").Name = "Sheet_" & i
If Err.Number = 1004 Then
i = i + 1
Resume NameAgain
End If
End Sub
Update after paxdiablo comment:
The above was a poor attempt at trying to replicate but simplify a problem I was having. The section of code I am working with is below:
Activate CheckBook to use ActiveWindow
CheckBook.Activate
Set DestSheet = CheckBook.Worksheets.Add(After:=CheckBook.Sheets(1))
On Error Resume Next
v = 1
NameAgain: DestSheet.Name = ExpBookName & "_" & v
If Err.Number = 1004 Then
v = v + 1
Resume NameAgain
End If
On Error GoTo 0
ActiveWindow.DisplayGridlines = False
Set DestCell = DestSheet.Range("A2")
So the solution is to move the On Error Resume Next to the label line and use GoTo in place of Resume.
The resume statement is a means to, from within an error handler, go back to some point in your main (non-error-handling) code and resume execution.
In this case, you've explicitly stated you want to automatically resume next in the event of an error.
This is functionally equivalent to (VB-like pseudo-code):
line:
on error goto handler
cause error
resume line ' not in an error handler at this point '
handler:
resume next
So you're not technically in an error handler at the point where you try to resume to the label.
The right statement for what you're trying to do would be a simple goto rather than resume.
A more correct solution is to write code that does not deliberately generate errors or which does not use Goto.
Public Function GetNextSheetName(ByVal ipWb As Excel.Workbook, ByVal ipStemName As String) As String
Dim mySheet As Variant
Dim mySD As Scripting.Dictionary
Set mySD = New Scripting.Dictionary
For Each mySheet In ipWb.Sheets
' mySd.Count is a dummy entry to satisfy
' the Key and Item requirements for .Add.
' we are only interested in the Keys
' for use with the .Exists method later
mySD.Add mySheet.Name, mySD.Count
Next
Do
DoEvents
Dim myIndex As Long
myIndex = myIndex + 1
Dim myNextSheetName As String
myNextSheetName = ipStemName + "_" & CStr(myIndex)
Loop While mySD.Exists(myNextSheetName)
GetNextSheetName = myNextSheetName
End Function
Which now allows
Set DestSheet = checkbook.Worksheets.Add(After:=checkbook.Sheets(1))
DestSheet.Name = GetNextSheetName(checkbook, ExpBookName)
ActiveWindow.DisplayGridlines = False
Set DestCell = DestSheet.Range("A2")

VBA cannot loop through rows

I have a piece of code that loops thru my rows in excel and find the first character of a string in a particular column. While that works perfectly with i = i + 1 , when the first condition passed, it will then execute another action. Inside the action, sometime i have errors happening, thus i put On error Handlers so that i can move on to the next row. Here is the code that i have.
On Error GoTo errHandler
Check:
While Cells(7 + i, 1).Value <> ""
firstChar = Left(Cells(7 + i, 6).Value, 1)
If firstChar = "A" Then
.findById("wnd[0]/tbar[0]/okcd").Text = "/nmm02"
.findById("wnd[0]").sendVKey 0
.findById("wnd[0]/usr/ctxtRMMG1-MATNR").Text = ""
.findById("wnd[0]/usr/ctxtRMMG1-MATNR").Text = Cells(7 + i, 2)
.findById("wnd[0]").sendVKey 0
.findById("wnd[1]/tbar[0]/btn[0]").press
.findById("wnd[1]/usr/ctxtRMMG1-LGTYP").Text = "AN1"
.findById("wnd[1]/usr/ctxtRMMG1-LGTYP").SetFocus
.findById("wnd[1]/usr/ctxtRMMG1-LGTYP").caretPosition = 3
.findById("wnd[1]/tbar[0]/btn[0]").press
.findById("wnd[0]/usr/tabsTABSPR1/tabpSP21/ssubTABFRA1:SAPLMGMM:2000/subSUB2:SAPLMGD1:2731/ctxtMLGN-PLKPT").Text = "AN1"
.findById("wnd[0]/usr/tabsTABSPR1/tabpSP21/ssubTABFRA1:SAPLMGMM:2000/subSUB3:SAPLMGD1:2733/ctxtMLGN-LTKZA").Text = "AN1"
.findById("wnd[0]/usr/tabsTABSPR1/tabpSP21/ssubTABFRA1:SAPLMGMM:2000/subSUB3:SAPLMGD1:2733/ctxtMLGN-LTKZE").Text = "AN1"
.findById("wnd[0]/usr/tabsTABSPR1/tabpSP21/ssubTABFRA1:SAPLMGMM:2000/subSUB3:SAPLMGD1:2733/ctxtMLGN-LTKZE").SetFocus
.findById("wnd[0]/usr/tabsTABSPR1/tabpSP21/ssubTABFRA1:SAPLMGMM:2000/subSUB3:SAPLMGD1:2733/ctxtMLGN-LTKZE").caretPosition = 3
.findById("wnd[0]/usr/tabsTABSPR1/tabpSP22").Select
.findById("wnd[0]/usr/tabsTABSPR1/tabpSP22/ssubTABFRA1:SAPLMGMM:2000/subSUB3:SAPLMGD1:2734/ctxtMLGT-LGPLA").Text = Cells(7 + i, 6)
.findById("wnd[0]/usr/tabsTABSPR1/tabpSP22/ssubTABFRA1:SAPLMGMM:2000/subSUB3:SAPLMGD1:2734/ctxtMLGT-LGPLA").SetFocus
.findById("wnd[0]/usr/tabsTABSPR1/tabpSP22/ssubTABFRA1:SAPLMGMM:2000/subSUB3:SAPLMGD1:2734/ctxtMLGT-LGPLA").caretPosition = 8
.findById("wnd[0]/tbar[0]/btn[11]").press
.findById("wnd[0]/tbar[0]/okcd").Text = "/N"
.findById("wnd[0]").sendVKey 0
ElseIf firstChar = "B" Then
#another action
ElseIf firstChar = "C" Then
#another action
Else:
End If
i = i + 1
errHandler:
Cells(7 + i, 9).Value = "Error"
session.findById("wnd[2]/tbar[0]/btn[0]").press
session.findById("wnd[1]/tbar[0]/btn[12]").press
session.findById("wnd[0]/tbar[0]/okcd").Text = "/n"
GoTo Check
Basically, if there are no errors in between the actions, the loop works fine and go thru to another rows, if there is an error, errHandler's action will be performed but it will goes back to check: and perform on the same row again. Also just to let everyone know, the firstChar that im searching for does not exist in every row, it can be on row 10 and the next one will be 55th.
Also, i tested by adding i = i + 1 to errHandler and yes the first error was successfully avoided by going to the next row. However, if it meets another error, the same thing happens again, executing on the same row. Does it have anything to do with finding the char 'A','B','C'?
An error handler can only handle one error at a time. Your handler was not reset so it is encountering the second error while still handling the first.
The only way to reset the handler is by executing one of the following:
Resume
Exit Sub
Exit Function
Exit Property
On Error Goto -1
Note that Err.Clear and On Error Goto 0 will clear the error number but they will not reset the handier. Also, On Error Resume Next is not the same as Resume (above) so it will not reset you handler either.
Your solution: replace GoTo Check with Resume Check
Using Resume Check will reset the handler and send execution to the Label just like your GoTo. This will also fix the infinite loop your GoTo appears to cause. Presumably that code block should only be executed after raising an error so you may want to use Exit Sub or Exit Function above errHandler:.

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

Error Handling within a Loop for PivotTable object sorting

I've been trying to sort PivotTable objects in VBA using a function I've attempted:
Public Function PTSort(PTName As String, PTFieldName as String, SortArray As Variant)
Dim m as Integer: m = 1 'Row counter
Dim i as Long 'Dummy Variable for cycling
With ActiveSheet.PivotTables(PTName).PivotFields(PTFieldName)
.Parent.ManualUpdate = True
For i = LBound(SortArray) To UBound(SortArray)
With .PivotItems(SortArray(m - 1)) 'For in-code array
.Position = m
End With
m = m + 1
Next i
.Parent.ManualUpdate = False
End With
End Function
Whilst this works well with a known set of elements within SortArray, I have a master list to follow whilst sorting (so as to standardise a few orders accross several PivotTables), in which the PivotTable need not necessarily contain all said PivotItems. I have hence improved it to the following:
Sub PTSort(PTName As String, PTFieldName as String, SortArray As Variant)
Dim m as Integer: m = 1
Dim i as Long
Dim k As Integer: k = 1 'To cycle the position independently from the array in the event of disjoint.
With ActiveSheet.PivotTables(PTName).PivotFields(PTFieldName)
.Parent.ManualUpdate = True
For i = LBound(SortArray) To UBound(SortArray)
On Error GoTo ERRHANDLER:
With .PivotItems(SortArray(k)) 'After parsing from range of cells into VariantArray, then does one not require the "-1"
.Position = m
End With
m = m + 1
ExitHandler:
k = k + 1
Next i
.Parent.ManualUpdate = False
End With
GoTo ENDEND:
ERRHANDLER:
GoTo EXITHANDLER:
ENDEND:
End Sub
The OnError GoTo seems to only work once though, irregardless of how high up placed it?
Help would be greatly appreciated. Thanks in advance!
This is from MSDN on Visual Studio but I think it applies to VBA in the same way.
An "enabled" error handler is one that is turned on by an On Error statement. An "active" error handler is an enabled handler that is in the process of handling an error.
If an error occurs while an error handler is active (between the occurrence of the error and a Resume, Exit Sub, Exit Function, or Exit Property statement), the current procedure's error handler cannot handle the error. Control returns to the calling procedure
So after your code first gets to On Error GoTo ERRHANDLER, ERRHANDLER is enabled. Then when an error occurs ERRHANDLER is activated and handles the error. When you GoTo EXITHANDLER it stays active and still handles the error. The On Error GoTo ERRHANDLER has no effect at this point.
To re-enable the ERRHANDLER you need to use Resume EXITHANDLER instead of GoTo EXITHANDLER.
Edit on the Resume statement. There are three ways to use Resume: Resume, Resume Next and Resume label
Resume without an argument causes the code to resume at the line that caused the error. This obviously has to be used very careful as you must be absolutely certain that you fixed the problem or you'll end up in an infinite loop.
Resume Next causes the code to resume at the line after the line that caused the error.
Resume Label is almost the same as GoTo Label but with this you exit the error handler and resume normal code execution. The error handler is re-enabled.