VBA, Looping through Directory contains corrupt file, bypass? - vba

I have a macro that goes through a large directory of files and performs a task. However the macro stops when it gets to a certain file that has 'unreadable content'. (excel files)
What can I add to my code to skip these files? What area of my code do I place it?
Tried adding this to my code after i declare my variables, doesn't do anything though.
On Error Resume Next
Many thanks
EDIT~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Posting part of my vba code, just a note: 'UserInput' is a function. If you need more posted to better understand let me know and i'll post.
'get user input for files to search
Set fileNames = CreateObject("Scripting.Dictionary")
errCheck = UserInput.FileDialogDictionary(fileNames)
If errCheck Then
Exit Sub
End If
'''
For Each Key In fileNames 'loop through the dictionary
Debug.Print fileNames(Key)
Set wb = Workbooks.Open(fileNames(Key), CorruptLoad:=xlRepairFile)
wb.Application.Visible = False 'make it not visible
EDIT~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Going to upload full code. This is with the recommended changes.
Sub ladiesman()
'includes filling down
Dim wb As Workbook, fileNames As Object, errCheck As Boolean
Dim ws As Worksheet, wks As Worksheet, wksSummary As Worksheet
Dim y As Range, intRow As Long, i As Integer
Dim r As Range, lr As Long, myrg As Range, z As Range
Dim boolWritten As Boolean, lngNextRow As Long
Dim intColNode As Integer, intColScenario As Integer
Dim intColNext As Integer, lngStartRow As Long
Dim lngLastNode As Long, lngLastScen As Long
' Turn off screen updating and automatic calculation
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Create a new worksheet, if required
On Error Resume Next
Set wksSummary = ActiveWorkbook.Worksheets("Unique data")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = ActiveWorkbook.Worksheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
wksSummary.Name = "Unique data"
End If
' Set the initial output range, and assign column headers
With wksSummary
Set y = .Cells(.Rows.Count, 3).End(xlUp).Offset(1, 0)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row
.Range("A1:D1").Value = Array("File Name", "Sheet Name", "Node Name", "Scenario Name")
End With
'get user input for files to search
Set fileNames = CreateObject("Scripting.Dictionary")
errCheck = UserInput.FileDialogDictionary(fileNames)
If errCheck Then
Exit Sub
End If
'''
For Each Key In fileNames 'loop through the dictionary
On Error Resume Next
Set wb = Workbooks.Open(fileNames(Key))
If Err.Number <> 0 Then
Set wb = Nothing ' or set a boolean error flag
End If
On Error GoTo 0 ' or your custom error handler
If wb Is Nothing Then
Debug.Print "Error when loading " & fileNames(Key)
Else
Debug.Print "Successfully loaded " & fileNames(Key)
wb.Application.Visible = False 'make it not visible
' more working with wb
End If
' Check each sheet in turn
For Each ws In ActiveWorkbook.Worksheets
With ws
' Only action the sheet if it's not the 'Unique data' sheet
If .Name <> wksSummary.Name Then
boolWritten = False
' Find the Scenario column
intColScenario = 0
On Error Resume Next
intColScenario = WorksheetFunction.Match("scenarioName", .Rows(1), 0)
On Error GoTo 0
If intColScenario > 0 Then
' Only action if there is data in column E
If Application.WorksheetFunction.CountA(.Columns(intColScenario)) > 1 Then
' Find the next free column, in which the extract formula will be placed
intColNext = .Cells(1, .Columns.Count).End(xlToLeft).Column + 1
' Assign formulas to the next free column to identify the scenario name to the left of the first _ character
.Cells(1, intColNext).Value = "Test"
lr = .Cells(.Rows.Count, intColScenario).End(xlUp).Row
Set myrg = .Range(.Cells(2, intColNext), .Cells(lr, intColNext))
With myrg
.ClearContents
.FormulaR1C1 = "=IFERROR(LEFT(RC" & intColScenario & ",FIND(INDEX({""+"",""-"",""_"",""$"",""%""},1,MATCH(1,--(ISNUMBER(FIND({""+"",""-"",""_"",""$"",""%""},RC" & _
intColScenario & "))),0)), RC" & intColScenario & ")-1), RC" & intColScenario & ")"
.Value = .Value
End With
' Copy unique values from the formula column to the 'Unique data' sheet, and write sheet & file details
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).AdvancedFilter xlFilterCopy, , r, True
r.Offset(0, -2).Value = ws.Name
r.Offset(0, -3).Value = ws.Parent.Name
' Clear the interim results
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).ClearContents
' Delete the column header copied to the list
r.Delete Shift:=xlUp
boolWritten = True
End If
End If
' Find the Node column
intColNode = 0
On Error Resume Next
intColNode = WorksheetFunction.Match("node", .Rows(1), 0)
On Error GoTo 0
If intColNode > 0 Then
' Only action if there is data in column A
If Application.WorksheetFunction.CountA(.Columns(intColNode)) > 1 Then
lr = .Cells(.Rows.Count, intColNode).End(xlUp).Row
' Copy unique values from column A to the 'Unique data' sheet, and write sheet & file details (if not already written)
.Range(.Cells(1, intColNode), .Cells(lr, intColNode)).AdvancedFilter xlFilterCopy, , y, True
If Not boolWritten Then
y.Offset(0, -1).Value = ws.Name
y.Offset(0, -2).Value = ws.Parent.Name
End If
' Delete the column header copied to the list
y.Delete Shift:=xlUp
End If
End If
' Identify the next row, based on the most rows used in columns C & D
lngLastNode = wksSummary.Cells(wksSummary.Rows.Count, 3).End(xlUp).Row
lngLastScen = wksSummary.Cells(wksSummary.Rows.Count, 4).End(xlUp).Row
lngNextRow = WorksheetFunction.Max(lngLastNode, lngLastScen) + 1
If (lngNextRow - lngStartRow) > 1 Then
' Fill down the workbook and sheet names
z.Resize(lngNextRow - lngStartRow, 2).FillDown
If (lngNextRow - lngLastNode) > 1 Then
' Fill down the last Node value
wksSummary.Range(wksSummary.Cells(lngLastNode, 3), wksSummary.Cells(lngNextRow - 1, 3)).FillDown
End If
If (lngNextRow - lngLastScen) > 1 Then
' Fill down the last Scenario value
wksSummary.Range(wksSummary.Cells(lngLastScen, 4), wksSummary.Cells(lngNextRow - 1, 4)).FillDown
End If
End If
Set y = wksSummary.Cells(lngNextRow, 3)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row
End If
End With
Next ws
wb.Close savechanges:=False 'close the workbook do not save
Set wb = Nothing 'release the object
Next 'End of the fileNames loop
Set fileNames = Nothing
' Autofit column widths of the report
wksSummary.Range("A1:D1").EntireColumn.AutoFit
' Reset system settings
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
.Visible = True
End With
End Sub

If you want to skip the unreadable file, you should get rid of CorruptLoad:=xlRepairFile (apparently it doesn't work for your files anyways), and use On Error Resume Next directly before trying to open the file.
Like this:
On Error Resume Next
Set wb = Workbooks.Open(fileNames(Key))
If Err.Number <> 0 Then
Set wb = Nothing ' or set a boolean error flag
End If
On Error GoTo 0 ' or your custom error handler
If wb Is Nothing Then
Debug.Print "Error when loading " & fileNames(Key)
Else
Debug.Print "Successfully loaded " & fileNames(Key)
wb.Application.Visible = False 'make it not visible
' more working with wb
' all
' your
' code
' goes
' here :)
End If
Edit
All the code from
' Check each sheet in turn
For Each ws In ActiveWorkbook.Worksheets
(you should use wb here instead of ActiveWorkbook)
to
wb.Close savechanges:=False 'close the workbook do not save
Set wb = Nothing 'release the object
belongs in the Else part directly after (or rather instead of) my placeholder comment
' more working with wb
All of this should only be done, if the workbook has been successfully loaded.
Edit 2
About wb vs ActiveWorkbook:
It improves the robustness of your code to avoid using ActiveWorkbook, ActiveSheet etc. as much as possible, especially when working with multiple workbooks. Some later changes to your code may make a different workbook active at the time you use it, and suddenly your code will fail. (Probably not in this function here, but it's a general rule of thumb.)
wb was just assigned to the opened workbook
Set wb = Workbooks.Open(fileNames(Key))
so it's good practice to use the wb variable for everything you do with that workbook.
For the skipped files:
Instead of
Debug.Print "Error when loading " & fileNames(Key)
simply collect them in a string
strErrorFiles = strErrorFiles & vbCrLf & fileNames(Key)
and later MsgBox that string. But note that MsgBox has a limit to the number of text it will show, so if there may be lots of error files, better write them to a sheet.

Related

table of contents vba- Visible sheets only

I'm trying to create a VBA code that will only create a ToC for visible sheets. I found some VBA code online and modified it to include Visible = True in the loop, but the hidden sheets are still displaying when I run the macro. I've included the code below and would appreciate any advice on tweaking it to only display visible sheets.
Sub TableOfContents_Create()
'Add a Table of Contents worksheets to easily navigate to any tab
Dim sht As Worksheet
Dim Content_sht As Worksheet
Dim myArray As Variant
Dim x As Long, y As Long
Dim shtName1 As String, shtName2 As String
Dim ContentName As String
'Inputs
ContentName = "Contents"
'Optimize Code
Application.DisplayAlerts = False
Application.ScreenUpdating = False
'Delete Contents Sheet if it already exists
On Error Resume Next
Worksheets("Contents").Activate
On Error GoTo 0
If ActiveSheet.Name = ContentName Then
myAnswer = MsgBox("A worksheet named [" & ContentName & _
"] has already been created, would you like to replace it?", vbYesNo)
'Did user select No or Cancel?
If myAnswer <> vbYes Then GoTo ExitSub
'Delete old Contents Tab
Worksheets(ContentName).Delete
End If
'Create New Contents Sheet
Worksheets.Add Before:=Worksheets(1)
'Set variable to Contents Sheet
Set Content_sht = ActiveSheet
'Format Contents Sheet
With Content_sht
.Name = ContentName
.Range("B1") = "Table of Contents"
.Range("B1").Font.Bold = True
End With
'Create Array list with sheet names (excluding Contents)
ReDim myArray(1 To Worksheets.Count - 1)
For Each sht In ActiveWorkbook.Worksheets
If sht.Name <> ContentName Then
myArray(x + 1) = sht.Name
x = x + 1
End If
Next sht
'Alphabetize Sheet Names in Array List
For x = LBound(myArray) To UBound(myArray)
For y = x To UBound(myArray)
If UCase(myArray(y)) < UCase(myArray(x)) Then
shtName1 = myArray(x)
shtName2 = myArray(y)
myArray(x) = shtName2
myArray(y) = shtName1
End If
Next y
Next x
'Create Table of Contents
For x = LBound(myArray) To UBound(myArray)
Set sht = Worksheets(myArray(x))
sht.Activate
With Content_sht
.Hyperlinks.Add .Cells(x + 2, 3), "", _
SubAddress:="'" & sht.Name & "'!A1", _
TextToDisplay:=sht.Name
.Cells(x + 2, 2).Value = x
End With
Next x
Content_sht.Activate
Content_sht.Columns(3).EntireColumn.AutoFit
ExitSub:
'Optimize Code
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
The .Visible property of the worksheet has three options:
As you can probably imagine, 0 is converted to False, and 1 or 2 to True. This causes errors, if you try to convert .Visible to a Boolean value.
Thus the idea is to loop only through worksheets, that are xlSheetVisible. Checking simply sht.Visible can lead to an error, if the sheet is xlSheetVeryHidden, because xlSheetVeryHidden is evaluated to True:
Public Sub TestMe()
Dim sht As Worksheet
Set sht = Worksheets(1)
sht.Visible = xlSheetVeryHidden
Debug.Print CBool(sht.Visible) 'prints true
End Sub
Thus use:
If sht.Visible = xlSheetVisible and sht.Name <> ContentName
loop only through visible sheets:
If sht.Name <> ContentName And sht.Visible Then

VBA, loop directory, file causing error

I have a code that loops through directory, but when it reaches a certain file, I get a run time error 13. Type mismatch.
debug line:
measurement = ws.Evaluate("sumproduct((" & ws.Range(ws.Cells(2, i), ws.Cells(lrw, i)).Address & "<>"""")+0)") / (lrw - 1)
All the other files in my directory work fine, just this one. contains 3 sheets. Any ideas? I can open the file fine. The code actually works halfway through the workbook and stops in sheet 2.
Sub stackmeup()
'added function to skip corrupt files works! Adding skipped files works.. and do something about 50%.
'changed lrw to long, doesnt skip those files now :)
Dim wb As Workbook, fileNames As Object, errCheck As Boolean 'part of loop
Dim ws As Worksheet
Dim resultSheet As Worksheet
Dim i As Long
Dim lco As Integer
Dim lrw As Long
Dim resultRow As Integer
Dim measurement As Double
'To compile skipped files
Dim wksSkipped As Worksheet
Set wksSkipped = ThisWorkbook.Worksheets("Skipped")
Set resultSheet = Application.ActiveSheet
resultRow = 1
'Optimize Macro Speed
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
'get user input for files to search
Set fileNames = CreateObject("Scripting.Dictionary")
errCheck = UserInput.FileDialogDictionary(fileNames)
If errCheck Then Exit Sub
For Each Key In fileNames 'loop through the dictionary
On Error Resume Next
Set wb = Workbooks.Open(fileNames(Key))
If Err.Number <> 0 Then
Set wb = Nothing ' or set a boolean error flag
End If
On Error GoTo 0 ' or custom error handler
If wb Is Nothing Then
wksSkipped.Cells(wksSkipped.Cells(wksSkipped.Rows.Count, "A").End(xlUp).Row + 1, 1) = fileNames(Key)
Else
Debug.Print "Successfully loaded " & fileNames(Key)
wb.Application.Visible = False 'make it not visible
For Each ws In wb.Worksheets
If Not Application.WorksheetFunction.CountA(ws.Cells) = 0 Then
'define the range to measure
lco = ws.Cells.Find("*", , xlValues, xlWhole, xlByColumns, xlPrevious, False).Column
lrw = ws.Columns("A:Y").Find("*", , xlValues, , xlRows, xlPrevious).Row
If lrw = 1 Then lrw = 2
For i = 1 To lco
measurement = ws.Evaluate("sumproduct((" & ws.Range(ws.Cells(2, i), ws.Cells(lrw, i)).Address & "<>"""")+0)") / (lrw - 1)
resultSheet.Cells(resultRow, 1).Value = wb.Name
resultSheet.Cells(resultRow, 2).Value = ws.Name
resultSheet.Cells(resultRow, 3).Value = ws.Cells(1, i).Value
resultSheet.Cells(resultRow, 4).Style = "Percent"
resultSheet.Cells(resultRow, 5).Value = measurement
resultRow = resultRow + 1
Next
End If
Next
wb.Application.Visible = True '' I added
wb.Close savechanges:=False 'close the workbook do not save
Set wb = Nothing 'release the object
End If
Next 'End of the fileNames loop
Set fileNames = Nothing
'Message Box when tasks are completed
MsgBox "Task Complete!"
ResetSettings:
'Reset Macro Optimization Settings
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
Function Col_Letter(lngCol As Long) As String
Dim vArr
vArr = Split(Cells(1, lngCol).Address(True, False), "$")
Col_Letter = vArr(0)
End Function
You can look for sheets which give an error using something like this:
Dim measurement As Variant
'...
'...
For i = 1 To lco
On Error Resume Next
measurement = ws.Evaluate("sumproduct((" & _
ws.Range(ws.Cells(2, i), ws.Cells(lrw, i)).Address & _
"<>"""")+0)") / (lrw - 1)
On Error Goto 0
With resultSheet.Rows(resultRow)
.Cells(1).Value = wb.Name
.Cells(2).Value = ws.Name
.Cells(3).Value = ws.Cells(1, i).Value
.Cells(4).Style = "Percent"
.Cells(5).Value = IIf(IsError(measurement),"Error!",measurement)
End With
resultRow = resultRow + 1
Next

Looping Filter Code through Directory

I have a code that does some advanced filters and creates a new sheet in the workbook. I need to add a code that can loop it through a directory and not miss any sheets.
Can anyone help with this? I've tried the generic ones online and just can't seem to get it to work on a workbook after the first one in the directory.
Sub Looper()
'a.t.v.5 + extra splitting of scen names(+,-,etc).
'looping dir
Dim ws As Worksheet, wks As Worksheet, wksSummary As Worksheet
Dim y As Range, intRow As Long, i As Integer
Dim r As Range, lr As Long, myrg As Range
Dim boolWritten As Boolean, lngNextRow As Long
Dim intColNode As Integer, intColScenario As Integer
Dim intColNext As Integer
' Turn off screen updating and automatic calculation
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Create a new worksheet, if required
On Error Resume Next
Set wksSummary = ActiveWorkbook.Worksheets("Unique data")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = ActiveWorkbook.Worksheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
wksSummary.Name = "Unique data"
End If
' Set the initial output range, and assign column headers
With wksSummary
Set y = .Cells(.Rows.Count, 3).End(xlUp).Offset(1, 0)
Set r = y.Offset(0, 1)
.Range("A1:D1").Value = Array("File Name", "Sheet Name", "Node Name", "Scenario Name")
End With
' Check each sheet in turn
For Each ws In ActiveWorkbook.Worksheets
With ws
' Only action the sheet if it's not the 'Unique data' sheet
If .Name <> wksSummary.Name Then
boolWritten = False
' Find the Scenario column
intColScenario = 0
On Error Resume Next
intColScenario = WorksheetFunction.Match("scenarioName", .Rows(1), 0)
On Error GoTo 0
If intColScenario > 0 Then
' Only action if there is data in column E
If Application.WorksheetFunction.CountA(.Columns(intColScenario)) > 1 Then
' Find the next free column, in which the extract formula will be placed
intColNext = .Cells(1, .Columns.Count).End(xlToLeft).Column + 1
' Assign formulas to the next free column to identify the scenario name to the left of the first _ character
.Cells(1, intColNext).Value = "Test"
lr = .Cells(.Rows.Count, intColScenario).End(xlUp).Row
Set myrg = .Range(.Cells(2, intColNext), .Cells(lr, intColNext))
With myrg
.ClearContents
.FormulaR1C1 = "=IFERROR(LEFT(RC" & intColScenario & ",FIND(INDEX({""+"",""-"",""_"",""$"",""%""},1,MATCH(1,--(ISNUMBER(FIND({""+"",""-"",""_"",""$"",""%""},RC" & intColScenario & "))),0)), RC" & intColScenario & ")-1), RC" & intColScenario & ")"
.Value = .Value
End With
' Copy unique values from the formula column to the 'Unique data' sheet, and write sheet & file details
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).AdvancedFilter xlFilterCopy, , r, True
r.Offset(0, -3).Value = ws.Name
r.Offset(0, -2).Value = ws.Parent.Name
' Clear the interim results
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).ClearContents
' Delete the column header copied to the list
r.Delete Shift:=xlUp
boolWritten = True
End If
End If
' Find the Node column
intColNode = 0
On Error Resume Next
intColNode = WorksheetFunction.Match("node", .Rows(1), 0)
On Error GoTo 0
If intColNode > 0 Then
' Only action if there is data in column A
If Application.WorksheetFunction.CountA(.Columns(intColNode)) > 1 Then
lr = .Cells(.Rows.Count, intColNode).End(xlUp).Row
' Copy unique values from column A to the 'Unique data' sheet, and write sheet & file details (if not already written)
.Range(.Cells(1, intColNode), .Cells(lr, intColNode)).AdvancedFilter xlFilterCopy, , y, True
If Not boolWritten Then
y.Offset(0, -2).Value = ws.Name
y.Offset(0, -1).Value = ws.Parent.Name
End If
' Delete the column header copied to the list
y.Delete Shift:=xlUp
End If
' Identify the next row, based on the most rows used in columns C & D
lngNextRow = WorksheetFunction.Max(wksSummary.Cells(wksSummary.Rows.Count, 3).End(xlUp).Row, wksSummary.Cells(wksSummary.Rows.Count, 4).End(xlUp).Row) + 1
Set y = wksSummary.Cells(lngNextRow, 3)
Set r = y.Offset(0, 1)
End If
End If
End With
Next ws
' Autofit column widths of the report
wksSummary.Range("A1:D1").EntireColumn.AutoFit
' Reset system settings
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
EDIT Aug 24
Sub looperv2()
Dim ws As Worksheet, wks As Worksheet, wksSummary As Worksheet
Dim y As Range, intRow As Long, i As Integer
Dim r As Range, lr As Long, myrg As Range, z As Range
Dim boolWritten As Boolean, lngNextRow As Long
Dim intColNode As Integer, intColScenario As Integer
Dim intColNext As Integer, lngStartRow As Long
' Turn off screen updating and automatic calculation
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Create a new worksheet, if required
On Error Resume Next
Set wksSummary = ActiveWorkbook.Worksheets("Unique data")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = ActiveWorkbook.Worksheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
wksSummary.Name = "Unique data"
End If
' Set the initial output range, and assign column headers
With wksSummary
Set y = .Cells(.Rows.Count, 3).End(xlUp).Offset(1, 0)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row
.Range("A1:D1").Value = Array("File Name", "Sheet Name", "Node Name", "Scenario Name")
End With
' Check each sheet in turn
For Each ws In ActiveWorkbook.Worksheets
With ws
' Only action the sheet if it's not the 'Unique data' sheet
If .Name <> wksSummary.Name Then
boolWritten = False
' Find the Scenario column
intColScenario = 0
On Error Resume Next
intColScenario = WorksheetFunction.Match("scenarioName", .Rows(1), 0)
On Error GoTo 0
If intColScenario > 0 Then
' Only action if there is data in column E
If Application.WorksheetFunction.CountA(.Columns(intColScenario)) > 1 Then
' Find the next free column, in which the extract formula will be placed
intColNext = .Cells(1, .Columns.Count).End(xlToLeft).Column + 1
' Assign formulas to the next free column to identify the scenario name to the left of the first _ character
.Cells(1, intColNext).Value = "Test"
lr = .Cells(.Rows.Count, intColScenario).End(xlUp).Row
Set myrg = .Range(.Cells(2, intColNext), .Cells(lr, intColNext))
With myrg
.ClearContents
.FormulaR1C1 = "=IFERROR(LEFT(RC" & intColScenario & ",FIND(INDEX({""+"",""-"",""_"",""$"",""%""},1,MATCH(1,--(ISNUMBER(FIND({""+"",""-"",""_"",""$"",""%""},RC" & _
intColScenario & "))),0)), RC" & intColScenario & ")-1), RC" & intColScenario & ")"
.Value = .Value
End With
' Copy unique values from the formula column to the 'Unique data' sheet, and write sheet & file details
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).AdvancedFilter xlFilterCopy, , r, True
r.Offset(0, -2).Value = ws.Name
r.Offset(0, -3).Value = ws.Parent.Name
' Clear the interim results
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).ClearContents
' Delete the column header copied to the list
r.Delete Shift:=xlUp
boolWritten = True
End If
End If
' Find the Node column
intColNode = 0
On Error Resume Next
intColNode = WorksheetFunction.Match("node", .Rows(1), 0)
On Error GoTo 0
If intColNode > 0 Then
' Only action if there is data in column A
If Application.WorksheetFunction.CountA(.Columns(intColNode)) > 1 Then
lr = .Cells(.Rows.Count, intColNode).End(xlUp).Row
' Copy unique values from column A to the 'Unique data' sheet, and write sheet & file details (if not already written)
.Range(.Cells(1, intColNode), .Cells(lr, intColNode)).AdvancedFilter xlFilterCopy, , y, True
If Not boolWritten Then
y.Offset(0, -1).Value = ws.Name
y.Offset(0, -2).Value = ws.Parent.Name
End If
' Delete the column header copied to the list
y.Delete Shift:=xlUp
End If
End If
' Identify the next row, based on the most rows used in columns C & D
lngNextRow = WorksheetFunction.Max(wksSummary.Cells(wksSummary.Rows.Count, 3).End(xlUp).Row, wksSummary.Cells(wksSummary.Rows.Count, 4).End(xlUp).Row) + 1
If (lngNextRow - lngStartRow) > 1 Then
z.Resize(lngNextRow - lngStartRow, 2).FillDown
End If
Set y = wksSummary.Cells(lngNextRow, 3)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row
End If
End With
Next ws
' Autofit column widths of the report
wksSummary.Range("A1:D1").EntireColumn.AutoFit
' Reset system settings
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
Here you go your code modified slightly:
Sub looperv2()
Dim wb As Workbook, fileNames As Object, errCheck As Boolean 'I have added this Sept 9, 2015
Dim ws As Worksheet, wks As Worksheet, wksSummary As Worksheet
Dim y As Range, intRow As Long, i As Integer
Dim r As Range, lr As Long, myrg As Range, z As Range
Dim boolWritten As Boolean, lngNextRow As Long
Dim intColNode As Integer, intColScenario As Integer
Dim intColNext As Integer, lngStartRow As Long
' Turn off screen updating and automatic calculation
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Create a new worksheet, if required
On Error Resume Next
Set wksSummary = ActiveWorkbook.Worksheets("Unique data")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = ActiveWorkbook.Worksheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
wksSummary.Name = "Unique data"
End If
' Set the initial output range, and assign column headers
With wksSummary
Set y = .Cells(.Rows.Count, 3).End(xlUp).Offset(1, 0)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row.Range("A1:D1").Value = Array("File Name", "Sheet Name", "Node Name", "Scenario Name")
End With
'Get User input for files to search 'I added the below Sept 9, 2015
Set fileNames = CreateObject("Scripting.Dictionary") 'I added the below Sept 9, 2015
errCheck = UserInput.FileDialogDictionary(fileNames) 'I added the below Sept 9, 2015
If errCheck Then 'I added the below Sept 9, 2015
Exit Sub 'I added the below Sept 9, 2015
End If 'I added the below Sept 9, 2015
'''
For Each Key In fileNames 'loop through the dictionary I added the below Sept 9, 2015
Set wb = Workbooks.Open(fileNames(Key)) 'I added the below Sept 9, 2015
wb.Application.Visible = False 'make it not visible I added the below Sept 9, 2015
' Check each sheet in turn
For Each ws In ActiveWorkbook.Worksheets
With ws
' Only action the sheet if it's not the 'Unique data' sheet
If .Name <> wksSummary.Name Then
boolWritten = False
' Find the Scenario column
intColScenario = 0
On Error Resume Next
intColScenario = WorksheetFunction.Match("scenarioName", .Rows(1), 0)
On Error GoTo 0
If intColScenario > 0 Then
' Only action if there is data in column E
If Application.WorksheetFunction.CountA(.Columns(intColScenario)) > 1 Then
' Find the next free column, in which the extract formula will be placed
intColNext = .Cells(1, .Columns.Count).End(xlToLeft).Column + 1
' Assign formulas to the next free column to identify the scenario name to the left of the first _ character
.Cells(1, intColNext).Value = "Test"
lr = .Cells(.Rows.Count, intColScenario).End(xlUp).Row
Set myrg = .Range(.Cells(2, intColNext), .Cells(lr, intColNext))
With myrg
.ClearContents
.FormulaR1C1 = "=IFERROR(LEFT(RC" & intColScenario & ",FIND(INDEX({""+"",""-"",""_"",""$"",""%""},1,MATCH(1,--(ISNUMBER(FIND({""+"",""-"",""_"",""$"",""%""},RC" & _
intColScenario & "))),0)), RC" & intColScenario & ")-1), RC" & intColScenario & ")"
.Value = .Value
End With
' Copy unique values from the formula column to the 'Unique data' sheet, and write sheet & file details
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).AdvancedFilter xlFilterCopy, , r, True
r.Offset(0, -2).Value = ws.Name
r.Offset(0, -3).Value = ws.Parent.Name
' Clear the interim results
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).ClearContents
' Delete the column header copied to the list
r.Delete Shift:=xlUp
boolWritten = True
End If
End If
' Find the Node column
intColNode = 0
On Error Resume Next
intColNode = WorksheetFunction.Match("node", .Rows(1), 0)
On Error GoTo 0
If intColNode > 0 Then
' Only action if there is data in column A
If Application.WorksheetFunction.CountA(.Columns(intColNode)) > 1 Then
lr = .Cells(.Rows.Count, intColNode).End(xlUp).Row
' Copy unique values from column A to the 'Unique data' sheet, and write sheet & file details (if not already written)
.Range(.Cells(1, intColNode), .Cells(lr, intColNode)).AdvancedFilter xlFilterCopy, , y, True
If Not boolWritten Then
y.Offset(0, -1).Value = ws.Name
y.Offset(0, -2).Value = ws.Parent.Name
End If
' Delete the column header copied to the list
y.Delete Shift:=xlUp
End If
End If
' Identify the next row, based on the most rows used in columns C & D
lngNextRow = WorksheetFunction.Max(wksSummary.Cells(wksSummary.Rows.Count, 3).End(xlUp).Row, wksSummary.Cells(wksSummary.Rows.Count, 4).End(xlUp).Row) + 1
If (lngNextRow - lngStartRow) > 1 Then
z.Resize(lngNextRow - lngStartRow, 2).FillDown
End If
Set y = wksSummary.Cells(lngNextRow, 3)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row
End If
End With
Next ws
wb.Application.Visible = True '' I added this Sept 9, 2015
wb.Close savechanges:=False ' I added this Sept 9, 2015
Set wb = Nothing 'release the object ' I added this Sept 9, 2015
Next 'End of the fileNames loop ' I added this Sept 9, 2015
Set fileNames = Nothing ' I added this Sept 9, 2015
' Autofit column widths of the report
wksSummary.Range("A1:D1").EntireColumn.AutoFit
' Reset system settings
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
and my file dialog code which I reused because it was already written. If you want to use a folder location you can use the file dialog folder picker option. Then just use a dictionary and loop through all files in the directory I suggest using the dir function and test for .xls or something like that.
Function FileDialogDictionary(ByRef file As Object) As Boolean ' returns true if the user cancels
'Declare a variable as a FileDialog object.
Dim fd As FileDialog
Dim item As Variant
Dim i As Long
'Create a FileDialog object as a File Picker dialog box.
file.RemoveAll 'clear the dictionary
Set fd = Application.FileDialog(msoFileDialogFilePicker)
'Declare a variable to contain the path
'of each selected item. Even though the path is a String,
'the variable must be a Variant because For Each...Next
'routines only work with Variants and Objects.
'Use a With...End With block to reference the FileDialog object.
With fd
'Use the Show method to display the File Picker dialog box and return the user's action.
'The user pressed the action button.
.Title = "Select Excel Workbooks" 'Change this to suit your purpose
.AllowMultiSelect = True
.Filters.Clear
.Filters.Add "Microsoft Excel files", "*.xlsx,*.xls"
If .Show = -1 Then
'Step through each string in the FileDialogSelectedItems collection.
For Each item In .SelectedItems 'loop through all selected and add to dictionary
i = i + 1
file.Add i, item
Next item
FileDialogDictionary = False
'The user pressed Cancel.
Else
FileDialogDictionary = True
Set fd = Nothing
Exit Function
End If
End With
Set fd = Nothing 'Set the object variable to Nothing.
End Function
Something like this would work I think:
Dim incomingFolderPath = "YOUR DIRECTORY HERE"
Dim archiveFolderPath As String = "Archive directory here"
While Directory.GetFiles(incomingFolderPath).Length > 0
Dim myFile as string = Dir(incomingFolderPath & "\*.*")
Dim fileToOpen As String = incomingFolderPath + myFile
'Logic here
System.IO.File.Move(fileToOpen, archiveFolderPath)
End While
The idea is it would check to see if the folder has anything in it, if it does it would use your logic then move that file to another location. It will loop through this until all files are moved.
Not sure if this is exactly what you're after but it should help.

If a worksheet is not present or blank, go to next iteration in a for loop

Below is a code to compile data from a specific sheet "Repair Summary by Location" of multiple workbooks into the macrobook worksheet "Repair Summary".
There are workbooks that don't have any data on "Repair summary by Location". Macro should do nothing but skip to next workbook.
Additionally, if the sheet is present, BUT it is empty then also macro should do the same as above. Below is the code.
'set up the output workbook
Set OutBook = ThisWorkbook 'Worksheets.Add
Set OutSheet = OutBook.Sheets.Add
OutSheet.Name = "Repair Summary"
Set OutSheet = OutBook.Sheets(1)
'loop through all files
For FileIdx = 1 To TargetFiles.SelectedItems.Count
'open the file and assign the workbook & worksheet
Set DataBook = Workbooks.Open(TargetFiles.SelectedItems(FileIdx))
Set DataSheet = DataBook.Sheets("Repair Summary by Location")
'identify row/column boundaries
LastDataRow = DataSheet.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
LastDataCol = DataSheet.Cells.Find("*", SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
'if this is the first go-round, include the header
If FileIdx = 1 Then
Set DataRng = Range(DataSheet.Cells(HeaderRow, 1), DataSheet.Cells(LastDataRow, LastDataCol))
Set OutRng = Range(OutSheet.Cells(HeaderRow, 1), OutSheet.Cells(LastDataRow, LastDataCol))
'if this is NOT the first go-round, then skip the header
Else
Set DataRng = Range(DataSheet.Cells(HeaderRow, 1), DataSheet.Cells(LastDataRow, LastDataCol))
Set OutRng = Range(OutSheet.Cells(LastOutRow + 2, 1), OutSheet.Cells(LastOutRow + 2 + LastDataRow, LastDataCol))
End If
'copy the data to the outbook
DataRng.Copy OutRng
'close the data book without saving
DataBook.Close False
'update the last outbook row
LastOutRow = OutSheet.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Next FileIdx
This is the alternative to #Tim's solution:
Public Function getSheet(ByVal wsName As String, Optional wb As Workbook = Nothing) As Worksheet
Dim ws As Worksheet
If Len(wsName) > 0 Then
If wb Is Nothing Then Set wb = ActiveWorkbook
For Each ws In wb.Worksheets
If ws.Name = wsName Then
Set getSheet = ws
Exit Function
End If
Next
End If
End Function
and to check that sheet exists and is not empty:
Dim ws As Worksheet
Set ws = getSheet("Repair Summary by Location")
If Not ws Is Nothing Then 'validates if Worksheet exists
If WorksheetFunction.CountA(ws) > 0 Then 'validates if Worksheet is not empty
...
See #rory's answer in the above link. Use that with Application.WorksheetFunction.CountA() and combine them... Just 4 lines of code...
Further to my comment, here are the 4 lines of code
If Evaluate("ISREF('" & sName & "'!A1)") Then '<~~ If sheet exists
If Application.WorksheetFunction.CountA(Sheets(sName).Cells) > 0 Then '<~~ If not empty
'
'~~> Your code
'
End If
End If
You can on error resume next but I would recommend against blanket solutions. I would use a loop once opening the book to look for the sheet using a function. Something like this:
Function FoundSheet(MySheetName as string) As Boolean
Dim WS as Worksheet
FoundSheet = False
For each WS in worksheets
If WS.Name = MySheetName then
FoundSheet = True
Exit for
End if
Next
End Function
This function returns a true or a false (As Boolean) and you would use this in your code like this:
If FoundSheet("YourSheetName") then 'Don't need = True or = False on the test as it is a boolean
'Your code goes here Start with a test, select it then see if there is data
End if
I free hand typed the code so there may be a typo or two but I am sure you can debug it.
Here is a very crude example of how it can work (I ran this against a new workbook with Sheet1 and Sheet2 in there but no Sheet3):
Sub testFunc()
Dim X As Long
For X = 1 To 3
MsgBox "Sheet" & X & " exists: " & FoundSheet("Sheet" & X)
Next
End Sub

VBA, Looping through Directory Issues

I have the following macro which does a calculation through a directory to each workbook - it's giving me an error at If wks.Name <> .Name Then ,
any suggestions or any other code I could be using to apply code to my directory?
Sub DirectoryExtractFilteredValues()
'PURPOSE: To loop through all Excel files in a user specified folder and perform a set task on them
'Loops trough all files in dir, error. Louisv4 in this.
Dim wb As Workbook
Dim myPath As String
Dim myFile As String
Dim myExtension As String
Dim FldrPicker As FileDialog
'Optimize Macro Speed
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
'Retrieve Target Folder Path From User
Set FldrPicker = Application.FileDialog(msoFileDialogFolderPicker)
With FldrPicker
.Title = "Select A Target Folder"
.AllowMultiSelect = False
If .Show <> -1 Then GoTo NextCode
myPath = .SelectedItems(1) & "\"
End With
'In Case of Cancel
NextCode:
myPath = myPath
If myPath = "" Then GoTo ResetSettings
'Target File Extension (must include wildcard "*")
myExtension = "*.xls"
'Target Path with Ending Extention
myFile = Dir(myPath & myExtension)
'Loop through each Excel file in folder
Do While myFile <> ""
'Set variable equal to opened workbook
Set wb = Workbooks.Open(filename:=myPath & myFile)
Dim wks As Excel.Worksheet
Dim wksSummary As Excel.Worksheet
'----------------------------------------------------------------------------------
'edited so it shows in the 3rd column row +1. Add the header and sheet name macro to this
On Error Resume Next
Set wksSummary = Excel.ActiveWorkbook.Worksheets("Unique data")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = Excel.ActiveWorkbook.Worksheets.Add
wksSummary.Name = "Unique data"
End If
'Iterate through all the worksheets, but skip [Summary] worksheet.
For Each wks In Excel.ActiveWorkbook.Worksheets
With wksSummary
If wks.Name <> .Name Then
If Application.WorksheetFunction.CountA(wks.Range("C:C")) Then
Dim r As Range
' Get the first cell of our destination range...
Set r = .Cells(.Cells(.Rows.Count, 3).End(xlUp).Row + 1, 3)
' Perform the unique copy...
If WorksheetFunction.CountA(wks.Range("C:C")) > 1 Then
wks.Range("C:C").AdvancedFilter xlFilterCopy, , r, True
End If
' Remove the first cell at the destination range...
r.Delete xlShiftUp
End If
End If
End With
Next wks
'Headers
Range("A1").Value = "File Name "
Range("B1").Value = "Sheet Name "
Range("C1").Value = "Column Name"
Dim intRow As Long: intRow = 2
For i = 1 To Sheets.Count
If Sheets(i).Name <> ActiveSheet.Name Then
Cells(intRow, 2) = Sheets(i).Name
Cells(intRow, 1) = ActiveWorkbook.Name
intRow = intRow + 1
End If
Next i
'Save and Close Workbook
wb.Close SaveChanges:=True
'Get next file name
myFile = Dir
Loop
'Message Box when tasks are completed
MsgBox "Task Complete!"
ResetSettings:
'Reset Macro Optimization Settings
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
EDIT: New code. Can anyone help with this? I tried the above directory code with this new code in the middle and tried making adjustments, cant get it to work.
Sub looper()
'a.t.v.5 + extra splitting of scen names(+,-,etc).
'looping dir
Dim ws As Worksheet, wks As Worksheet, wksSummary As Worksheet
Dim y As Range, intRow As Long, i As Integer
Dim r As Range, lr As Long, myrg As Range
Dim boolWritten As Boolean, lngNextRow As Long
Dim intColNode As Integer, intColScenario As Integer
Dim intColNext As Integer
' Turn off screen updating and automatic calculation
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Create a new worksheet, if required
On Error Resume Next
Set wksSummary = ActiveWorkbook.Worksheets("Unique data")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = ActiveWorkbook.Worksheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
wksSummary.Name = "Unique data"
End If
' Set the initial output range, and assign column headers
With wksSummary
Set y = .Cells(.Rows.Count, 3).End(xlUp).Offset(1, 0)
Set r = y.Offset(0, 1)
.Range("A1:D1").Value = Array("File Name", "Sheet Name", "Node Name", "Scenario Name")
End With
' Check each sheet in turn
For Each ws In ActiveWorkbook.Worksheets
With ws
' Only action the sheet if it's not the 'Unique data' sheet
If .Name <> wksSummary.Name Then
boolWritten = False
' Find the Scenario column
intColScenario = 0
On Error Resume Next
intColScenario = WorksheetFunction.Match("scenarioName", .Rows(1), 0)
On Error GoTo 0
If intColScenario > 0 Then
' Only action if there is data in column E
If Application.WorksheetFunction.CountA(.Columns(intColScenario)) > 1 Then
' Find the next free column, in which the extract formula will be placed
intColNext = .Cells(1, .Columns.Count).End(xlToLeft).Column + 1
' Assign formulas to the next free column to identify the scenario name to the left of the first _ character
.Cells(1, intColNext).Value = "Test"
lr = .Cells(.Rows.Count, intColScenario).End(xlUp).Row
Set myrg = .Range(.Cells(2, intColNext), .Cells(lr, intColNext))
With myrg
.ClearContents
.FormulaR1C1 = "=IFERROR(LEFT(RC" & intColScenario & ",FIND(INDEX({""+"",""-"",""_"",""$"",""%""},1,MATCH(1,--(ISNUMBER(FIND({""+"",""-"",""_"",""$"",""%""},RC" & intColScenario & "))),0)), RC" & intColScenario & ")-1), RC" & intColScenario & ")"
.Value = .Value
End With
' Copy unique values from the formula column to the 'Unique data' sheet, and write sheet & file details
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).AdvancedFilter xlFilterCopy, , r, True
r.Offset(0, -3).Value = ws.Name
r.Offset(0, -2).Value = ws.Parent.Name
' Clear the interim results
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).ClearContents
' Delete the column header copied to the list
r.Delete Shift:=xlUp
boolWritten = True
End If
End If
' Find the Node column
intColNode = 0
On Error Resume Next
intColNode = WorksheetFunction.Match("node", .Rows(1), 0)
On Error GoTo 0
If intColNode > 0 Then
' Only action if there is data in column A
If Application.WorksheetFunction.CountA(.Columns(intColNode)) > 1 Then
lr = .Cells(.Rows.Count, intColNode).End(xlUp).Row
' Copy unique values from column A to the 'Unique data' sheet, and write sheet & file details (if not already written)
.Range(.Cells(1, intColNode), .Cells(lr, intColNode)).AdvancedFilter xlFilterCopy, , y, True
If Not boolWritten Then
y.Offset(0, -2).Value = ws.Name
y.Offset(0, -1).Value = ws.Parent.Name
End If
' Delete the column header copied to the list
y.Delete Shift:=xlUp
End If
' Identify the next row, based on the most rows used in columns C & D
lngNextRow = WorksheetFunction.Max(wksSummary.Cells(wksSummary.Rows.Count, 3).End(xlUp).Row, wksSummary.Cells(wksSummary.Rows.Count, 4).End(xlUp).Row) + 1
Set y = wksSummary.Cells(lngNextRow, 3)
Set r = y.Offset(0, 1)
End If
End If
End With
Next ws
' Autofit column widths of the report
wksSummary.Range("A1:D1").EntireColumn.AutoFit
' Reset system settings
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub