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
Related
Struggle with a sub to search through sheet for #N/A! or #REF! errors.
It has to popup a msgbox when at least one error found. If no errors, should execute other sub - sprzedaz2.
As long as there is at least one error, it works okay. But for a sheet without errors, it breaks and I can't think of a solution.
What I have so far:
Sub X_SPR_sprawdzbledy()
Application.Goto Workbooks("generator_komunikatow.xlsm").Sheets("komunikat_OS_sprzedaz").Range("a1")
On Error Resume Next
On Error GoTo 0
If Sheet1.UsedRange.SpecialCells(xlCellTypeFormulas, xlErrors).Cells.Count = 0 Then
Call sprzedaz2
Else:
MsgBox ("UWAGA! Znaleziono " & Sheet1.UsedRange.SpecialCells(xlCellTypeFormulas, xlErrors).Cells.Count & " bledow!" & vbNewLine & vbNewLine & "SPRAWDZ KOMORKI Z #N/A! lub #REF!")
End If
End Sub
Main issue: If SpecialCells doesn't find anything (in your case, no cells with errors), it will throw a runtime error, you need to catch this error (a Range never can have 0 cells). The following code assigns the result of SpecialCells to a variable. If it fails because nothing was found, the variable remains unassigned which means it still holds Nothing.
Dim errorCells As Range
On Error Resume Next
Set errorCells = Sheet1.UsedRange.SpecialCells(xlCellTypeFormulas, xlErrors).Cells
On Error GoTo 0
If errorCells Is Nothing Then ' No errors in cells
Call sprzedaz2
Else
MsgBox "UWAGA! Znaleziono " & errorCells.Count & " bledow!" & vbNewLine & vbNewLine & "SPRAWDZ KOMORKI Z #N/A! lub #REF!"
End If
N.B. get rid of the : after the Else...
I was wondering whether there is a smarter/more accurate method of debugging userform initializations in VBA that are called from a specific module.
Because when I write an erroneously piece of code in the Userform_initialisatize, I the error I receive states an error, but not where it occurs, the highlighted piece of code is simply call Userform_intitialize and I'm left guessing which piece of code in Sub Userform_intitialize contains the error.
So incrementally building a userform with test runs after every small addition of code work effectively to create a stable Userform initilisation code. Because I know what I changed since the last successful run, but it would save quite some time if I immediately know where the error occurs, especially in cases where trial runs consume a lot of time.
So are there ways to extract in which line the exact error occurs within a called Sub Userform_intitialize in vba Excel 2016?
As #A.S.H already mentioned the handler of Initialize event should remain private. Thats because the Initialize event itself is private so it is intended to be handled by the instances itself and not by anybody else.
So are there ways to extract in which line the exact error occurs within a called Sub Userform_intitialize in vba Excel 2016?
Regarding your question, yes it is still possible to extract the line number where the error occurred. But the lines needs to be part of the source code. Then the function VBA.Information.Erl can be used to get the line number. Example:
UserForm module
Option Explicit
Private Const MODULE_NAME As String = "MyUserForm."
Private Sub UserForm_Initialize()
On Error GoTo Error_In_UserForm_Initialize
1 Const procName As String = "UserForm_Initialize"
' Some code here
2 Dim d As Date
3 d = Now
4 MsgBox "Hi from my userform! 'Now' is '" & d & "'", vbInformation, "Info"
' Here error occures, max. value of Integer is 32.767
5 Dim i As Integer
6 i = 40000
7 Exit Sub
Error_In_UserForm_Initialize:
8 Dim errorDescription As String
9 With Err
10 errorDescription = "Error '" & .Number & "'" & _
" with description '" & .Description & "'" & _
" occured in procedure '" & MODULE_NAME & procName & "'" & _
IIf(Erl <> 0, " on line '" & CStr(Erl) & "'.", ".")
11 End With
12 MsgBox errorDescription, vbCritical, "Error"
End Sub
Some additional readings about Erl here or here.
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.
I understand similar questions with these errors have been posted before, but I found nothing when it came to formatting tables so don't close this please. In my VBA code in MS Access 2013 it exports data from MS Access to Excel. 6 different queries get exported into 1 excel file, each on a different worksheet. This works fine. I then format each sheet to have all the data in a table. I have a form which lets the user choose the path to save the file. If it is the first time creating the file, it works properly. If it is the second time creating the file in that same directory, it doesn't work and it gives me the error:
Run-time error 1004: Method Range of object _Global failed
I figured this was because I was overwriting my file instead of deleting it and recreating it. So I added in some code to check if the file exists, and if it does, delete it. I added breakpoints and while running through this part of the code, I was watching my documents folder. The file successfully got deleted and then recreated which is what I wanted. It still gave me that error. I manually went to delete the file and then reran my code again. It worked properly.
How come I need to manually delete this file in order to rerun my code? Or is it something else that is causing the problem? Here is the important parts of my code as the whole thing is too long to post:
'Checks if a file exists, then checks if it is open
Private Sub checkFile(path As String)
Dim openCheck As Boolean
'If file exists, make sure it isn't open. If it doesn't, create it
If Dir(path) <> "" Then
openCheck = IsFileLocked(path)
If openCheck = True Then
MsgBox "Please close the file in " & path & " first and try again."
End
Else
deleteFile (path)
End If
Else
End If
End Sub
Sub deleteFile(ByVal FileToDelete As String)
SetAttr FileToDelete, vbNormal
Kill FileToDelete
End Sub
Private Sub dumpButton_Click()
On Error GoTo PROC_ERR
Dim path As String
Dim testBool As Boolean
path = pathLabel4.Caption
path = path & Format(Date, "yyyy-mm-dd") & ".xlsx"
checkFile (path)
dumpQueries (path)
formatFile (path)
'Error Handling
PROC_ERR:
If Err.Number = 2001 Then
MsgBox "A file may have been sent to " & path
Exit Sub
ElseIf Err.Number = 2501 Then
MsgBox "A file may have been sent to " & path
Exit Sub
ElseIf Err.Number = 3021 Then
MsgBox "A file may have been sent to " & path
Exit Sub
ElseIf Err.Number = 2302 Then
MsgBox "A file may have been sent to " & path
Exit Sub
ElseIf Err.Number = 0 Then
MsgBox "Your file has been stored in " & pathLabel4.Caption
Exit Sub
Else
MsgBox Err.Number & ": " & Err.Description & vbCrLf & vbCrLf & "New Error. Please contact the IT department."
End If
Private Sub dumpQueries(path As String)
Dim obj As AccessObject, dB As Object
Set dB = Application.CurrentData
For Each obj In dB.AllQueries
testBool = InStr(obj.name, "Sys")
If testBool <> True Then
If obj.name = "example1" Or obj.name = "example2" Then
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, obj.name, path, True, editWorksheetName(obj.name)
End If
End If
Next obj
End Sub
'Autofits the cells in every worksheet
Private Sub formatFile(path As String)
Dim Date1 As Date, strReportAddress As String
Dim objActiveWkb As Object, appExcel As Object
Set appExcel = CreateObject("Excel.Application")
appExcel.Visible = False
appExcel.Application.Workbooks.Open (path)
Set objActiveWkb = appExcel.Application.ActiveWorkbook
With objActiveWkb
Dim i As Integer
For i = 1 To .Worksheets.count
.Worksheets(i).Select
.Worksheets(i).Cells.EntireColumn.AutoFit
.Worksheets(i).ListObjects.Add(xlSrcRange, Range("A1").CurrentRegion, , xlYes).name = "myTable1"
Next
End With
appExcel.ActiveWindow.TabRatio = 0.7
objActiveWkb.Close savechanges:=True
appExcel.Application.Quit
Set objActiveWkb = Nothing: Set appExcel = Nothing
End Sub
The error occurs near the bottom of the code. It's the line:
.Worksheets(i).ListObjects.Add(xlSrcRange, Range("A1").CurrentRegion, , xlYes).name = "myTable1"
There may be a couple functions I left out but they work fine and shouldn't be needed for answering the question.
This is the only relevant code:
Set objActiveWkb = appExcel.Application.ActiveWorkbook
With objActiveWkb
Dim i As Integer
For i = 1 To .Worksheets.count
.Worksheets(i).Select
.Worksheets(i).Cells.EntireColumn.AutoFit
.Worksheets(i).ListObjects.Add(xlSrcRange, Range("A1").CurrentRegion, , xlYes).name = "myTable1"
Next
End With
Things get easier to follow when you trim the fluff away and start naming things - there's no need to .Select anything, appExcel is already an Application object, and there's no need to make a copy reference to the active workbook just to use in a With block, especially if that copy is going to be an Object variable anyway - if the copy were a Workbook object then you would at least get IntelliSense for its members...
Your source range is ambiguous. Range("A1") in Excel-VBA is an implicit reference to the active worksheet.. but this is Access-VBA, so there's no such thing, xlSrcRange is an enum value defined in the Excel object model, so if you don't have a reference to the Excel object model (you're late-binding this, right?), and Option Explicit isn't specified, then xlSrcRange is treated by VBA like just another undeclared/uninitialized variable, and therefore you're passing a 0 there, and the xlSrcRange enum value stands for a 1 - and 0 happens to be the underlying value for xlSrcExternal. Same with xlYes.
Since we cannot possibly guess what the actual source range is supposed to be from the code you posted, I'm leaving you with this:
Dim target As Object
Dim srcRange As Object
Set srcRange = TODO
With appExcel.ActiveWorkbook
Dim i As Integer
For i = 1 To .Worksheets.Count
.Worksheets(i).Cells.EntireColumn.AutoFit
Set target = .Worksheets(i).ListObjects.Add(1, srcRange, , 1)
If target Is Not Nothing Then target.Name = "myTable1"
Next
End With
Side question... why name the table myTable1 when Excel will already have named it Table1 anyway? Also note, if .Add fails, your code blows up with a runtime error 91 because you'd be calling .Add off Nothing. Verifying that the target is not Nothing before setting its Name will avoid that.
To answer your question in the comments:
#Mat'sMug is this what you were talking about? because it gives me this error: "438: Object doesn't support this property or method" Here's the code: .Worksheets(i).ListObjects.Add(SourceType:=xlSrcRange, Source:=.Cells(1).CurrentRegion, _ XlListObjectHasHeaders:=xlYes, TableStylename:="TableStyleMedium1").name = "Table"
The reason this throws a 438 is because your With block variable is a Workbook object, and a Workbook object doesn't have a .Range member.
What I was talking about, is that in Excel VBA unqualified calls to Range, Row, Column, and Cells are implicitly referencing the ActiveSheet, and unqualified calls to Worksheets, Sheets and Names are implicitly referencing the ActiveWorkbook - that's a recurrent problem in a lot of VBA code and a very common mistake to make. The solution is basically to say what you mean, and mean what you say; in this case the failure is on "mean what you say" - the unqualified Range("A1") call is, according to the error message, calling [_Globals].Range("A1")... which is weird because it implies that you're referencing the Excel object model library, which means your late-binding and Object variables could just as well be early-bound: why deal with Object variables and lack of IntelliSense when you're already referencing the library you're late-binding to?
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