I have the following(see below) "File Search Utility" macro that I have been using in Excel 2010. This macro searches through a specified folder of workbooks and returns the desired data (love this macro!).
In Excel 2010, the search (which searches 450+ files) takes about 2 minutes and displays the results AS they are found.
In Excel 2016, the search takes more than double the time, and no results are displayed until the macro has completely run through all of the files in the folder.
I am a novice to intermediate macro programmer at best (i.e. I know enough to be dangerous). Any help to tweak this code would be greatly appreciated.
Here is the Code:
Option Explicit
Public Sub SearchButton_Click()
Dim astrWorkbooks() As String
Dim strPartNumber As String
Dim strFolderPath As String
Dim vntWorkbooks As Variant
Dim j As Long
On Error GoTo ErrHandler
If Not ValidateData("PartNumber", strPartNumber) Then
MsgBox "Part number has not been entered.", vbExclamation
Exit Sub
End If
If Not ValidateData("SearchFolder", strFolderPath) Then
MsgBox "Search folder has not been entered.", vbExclamation
Exit Sub
End If
Call ClearResultsTable
If Not FolderExists(strFolderPath) Then
MsgBox "Search folder does not exist.", vbExclamation
Exit Sub
End If
vntWorkbooks = GetAllWorkbooks(strFolderPath)
If IsEmpty(vntWorkbooks) Then
MsgBox "Search folder does not contain any Excel workbooks.", vbExclamation
Exit Sub
End If
astrWorkbooks = vntWorkbooks
For j = LBound(astrWorkbooks) To UBound(astrWorkbooks)
Call SearchWorkbook(astrWorkbooks(j), strPartNumber)
Next j
MsgBox "Search has completed. Please check results table.", vbInformation
Exit Sub
ErrHandler:
MsgBox Err.Description, vbExclamation
End Sub
Private Function FolderExists(ByRef strFolderPath As String) As Boolean
On Error GoTo ErrHandler
If Right(strFolderPath, 1) <> Application.PathSeparator Then
strFolderPath = strFolderPath & Application.PathSeparator
End If
FolderExists = (Dir(strFolderPath, vbDirectory) <> "")
Exit Function
ErrHandler:
FolderExists = False
End Function
Private Sub ClearResultsTable()
Dim tblResults As ListObject
Dim objFilter As AutoFilter
Dim rngBody As Range
Set tblResults = wksSearchUtility.ListObjects("Results")
Set objFilter = tblResults.AutoFilter
Set rngBody = tblResults.DataBodyRange
If Not objFilter Is Nothing Then
If objFilter.FilterMode Then
objFilter.ShowAllData
End If
End If
If Not rngBody Is Nothing Then
rngBody.Delete
End If
End Sub
Private Function ValidateData(ByVal strRangeName As String, ByRef strData As String) As Boolean
On Error GoTo ErrHandler
strData = UCase(Trim(wksSearchUtility.Range(strRangeName).Text))
ValidateData = (strData <> vbNullString)
Exit Function
ErrHandler:
ValidateData = False
End Function
Private Function GetAllWorkbooks(strFolderPath As String) As Variant
Dim lngWorkbookCount As Long
Dim astrWorkbooks() As String
Dim strFileName As String
Dim strFilePath As String
On Error GoTo ErrHandler
strFileName = Dir(strFolderPath & "*.xl*")
Do Until (strFileName = vbNullString)
lngWorkbookCount = lngWorkbookCount + 1
strFilePath = strFolderPath & strFileName
ReDim Preserve astrWorkbooks(1 To lngWorkbookCount)
astrWorkbooks(lngWorkbookCount) = strFilePath
strFileName = Dir()
Loop
If lngWorkbookCount > 0 Then
GetAllWorkbooks = astrWorkbooks
Else
GetAllWorkbooks = Empty
End If
Exit Function
ErrHandler:
GetAllWorkbooks = Empty
End Function
Private Sub SearchWorkbook(strFilePath As String, strPartNumber As String)
Dim sht As Worksheet
Dim wbk As Workbook
On Error GoTo ErrHandler
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Set wbk = Workbooks.Open(strFilePath, False)
For Each sht In wbk.Worksheets
Call SearchWorksheet(sht, strPartNumber)
Next sht
ExitProc:
On Error Resume Next
wbk.Close False
Application.DisplayAlerts = True
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Exit Sub
ErrHandler:
Resume ExitProc
End Sub
Private Sub SearchWorksheet(sht As Worksheet, strPartNumber As String)
Dim rngTableRow As Range
Dim cell As Range
On Error GoTo ErrHandler
For Each cell In Intersect(sht.Columns("B"), sht.UsedRange).Cells
If UCase(cell.Text) Like "*" & strPartNumber & "*" Then
Set rngTableRow = GetNextRow()
rngTableRow.Item(1).Value = sht.Parent.Name
rngTableRow.Item(2).Value = cell.Text
rngTableRow.Item(3).Value = cell.Offset(, -1).Value
rngTableRow.Item(4).Value = cell.Offset(, 6).Value
rngTableRow.Item(5).Value = cell.Offset(, 7).Value
rngTableRow.Item(6) = Range("I3")
End If
Next cell
Exit Sub
ErrHandler:
End Sub
Private Function GetNextRow() As Range
With wksSearchUtility.ListObjects("Results")
If .InsertRowRange Is Nothing Then
Set GetNextRow = .ListRows.Add.Range
Else
Set GetNextRow = .InsertRowRange
End If
End With
End Function
You are testing every single cell in column B, that's the performance killer. Check this post for how to do this using the find function, it will be WAY faster.
Find all matches in workbook using Excel VBA
Where the code in that answer defines loc, replace .cells with Intersect(sht.Columns("B"), sht.UsedRange)
it should read something like this:
Set Loc = Intersect(sht.Columns("B"), sht.UsedRange).Find(What:="Question?")
And obviously "Question" will become strPartNumber
Just wanted to include the solution mentioned by OP here as it is located on a different forum.
Option Explicit
Public Sub SearchButton_Click()
Dim astrWorkbooks() As String, strPartNumber As String, strFolderPath As String, vntWorkbooks As Variant
Dim j As Long, BlockSize As Long, myRng As Range, BigRng As Range, TempSht As Worksheet, i, myFormula As String, yyy As Range
Dim Drng As Range, SceRng As Range, Destn As Range, msg As String
Application.ScreenUpdating = False
On Error GoTo ErrHandler
If Not ValidateData("PartNumber", strPartNumber) Then
MsgBox "Part number has not been entered.", vbExclamation
Exit Sub
End If
If Not ValidateData("SearchFolder", strFolderPath) Then
MsgBox "Search folder has not been entered.", vbExclamation
Exit Sub
End If
Call ClearResultsTable
If Not FolderExists(strFolderPath) Then
MsgBox "Search folder does not exist.", vbExclamation
Exit Sub
End If
vntWorkbooks = GetAllWorkbooks(strFolderPath)
If IsEmpty(vntWorkbooks) Then
MsgBox "Search folder does not contain any Excel workbooks.", vbExclamation
Exit Sub
End If
Set TempSht = Sheets.Add
astrWorkbooks = vntWorkbooks
BlockSize = 37
For i = 1 To UBound(astrWorkbooks)
myFormula = "='" & strFolderPath & "[" & astrWorkbooks(i) & "]Invoice'!R2C1:R" & BlockSize + 1 & "C9"
Set myRng = TempSht.Range("B" & BlockSize * i - BlockSize + 1).Resize(BlockSize, 9)
myRng.FormulaArray = myFormula
myRng.Offset(, -1).Resize(, 1).Value = astrWorkbooks(i)
myFormula = "='" & strFolderPath & "[" & astrWorkbooks(i) & "]Invoice'!R3C9"
myRng.Columns(myRng.Columns.Count).Offset(, 1).FormulaR1C1 = myFormula
If BigRng Is Nothing Then Set BigRng = myRng Else Set BigRng = Union(BigRng, myRng)
Next i
Set BigRng = BigRng.Offset(, -1).Resize(, BigRng.Columns.Count + 2)
BigRng.Value = BigRng.Value
With TempSht
.Columns("D:H").Delete
.Columns("C:C").Cut
.Columns("B:B").Insert
BigRng.AutoFilter Field:=2, Criteria1:="=*" & strPartNumber & "*"
Set yyy = .AutoFilter.Range
If yyy.Columns(1).SpecialCells(xlCellTypeVisible).Count > 1 Then
Set Drng = .Range("A" & yyy.Rows.Count + 10)
yyy.Offset(1).Resize(yyy.Rows.Count - 1).Copy Drng
Set SceRng = Drng.CurrentRegion
Set Destn = GetNextRow.Resize(SceRng.Rows.Count)
Destn.Value = SceRng.Value
msg = "Search has completed. Please check results table."
Else
msg = "Search has completed. No results found"
End If
Application.DisplayAlerts = False: .Delete: Application.DisplayAlerts = True
End With
Application.ScreenUpdating = True
MsgBox msg, vbInformation
Exit Sub
ErrHandler:
Application.ScreenUpdating = True
MsgBox Err.Description, vbExclamation
End Sub
Private Function FolderExists(ByRef strFolderPath As String) As Boolean
On Error GoTo ErrHandler
If Right(strFolderPath, 1) <> Application.PathSeparator Then
strFolderPath = strFolderPath & Application.PathSeparator
End If
FolderExists = (Dir(strFolderPath, vbDirectory) <> "")
Exit Function
ErrHandler:
FolderExists = False
End Function
Private Sub ClearResultsTable()
Dim tblResults As ListObject
Dim objFilter As AutoFilter
Dim rngBody As Range
Set tblResults = wksSearchUtility.ListObjects("Results")
Set objFilter = tblResults.AutoFilter
Set rngBody = tblResults.DataBodyRange
If Not objFilter Is Nothing Then
If objFilter.FilterMode Then
objFilter.ShowAllData
End If
End If
If Not rngBody Is Nothing Then
rngBody.Delete
End If
End Sub
Private Function ValidateData(ByVal strRangeName As String, ByRef strData As String) As Boolean
On Error GoTo ErrHandler
strData = UCase(Trim(wksSearchUtility.Range(strRangeName).Text))
ValidateData = (strData <> vbNullString)
Exit Function
ErrHandler:
ValidateData = False
End Function
Private Function GetNextRow() As Range
With wksSearchUtility.ListObjects("Results")
If .InsertRowRange Is Nothing Then
Set GetNextRow = .ListRows.Add.Range
Else
Set GetNextRow = .InsertRowRange
End If
End With
End Function
Private Function GetAllWorkbooks(strFolderPath As String) As Variant
Dim lngWorkbookCount As Long
Dim astrWorkbooks() As String
Dim strFileName As String
Dim strFilePath As String
On Error GoTo ErrHandler
strFileName = Dir(strFolderPath & "*.xl*")
Do Until (strFileName = vbNullString)
lngWorkbookCount = lngWorkbookCount + 1
ReDim Preserve astrWorkbooks(1 To lngWorkbookCount)
astrWorkbooks(lngWorkbookCount) = strFileName
strFileName = Dir()
Loop
If lngWorkbookCount > 0 Then
GetAllWorkbooks = astrWorkbooks
Else
GetAllWorkbooks = Empty
End If
Exit Function
ErrHandler:
GetAllWorkbooks = Empty
End Function
Related
function in this macro checking only opened excel for exsiting Sheet "economy" , but i need to check for exsisting this Sheet in each excel file i checking in folder and subfolders.
How i can edit this to check sheet name in not current macro excel file but in all files that i opened in sub "ListFilesInFolder"?
Sub MainList()
Set folder = Application.FileDialog(msoFileDialogFolderPicker)
If folder.Show <> -1 Then Exit Sub
xDir = folder.SelectedItems(1)
Call ListFilesInFolder(xDir, True)
End Sub
Function WorksheetExists(shtName As String, Optional wb As Workbook) As Boolean
Dim sht As Worksheet
If wb Is Nothing Then Set wb = ThisWorkbook
On Error Resume Next
Set sht = wb.Sheets(shtName)
On Error GoTo 0
WorksheetExists = Not sht Is Nothing
End Function
Sub ListFilesInFolder(ByVal xFolderName As String, ByVal xIsSubfolders As Boolean)
Dim xFileSystemObject As Object
Dim xFolder As Object
Dim xSubFolder As Object
Dim xFile As Object
Dim rowIndex As Long
Set xFileSystemObject = CreateObject("Scripting.FileSystemObject")
Set xFolder = xFileSystemObject.GetFolder(xFolderName)
rowIndex = Application.ActiveSheet.Range("A65536").End(xlUp).Row + 1
For Each xFile In xFolder.Files
If WorksheetExists("economy") = True Then
Application.ActiveSheet.Cells(rowIndex, 1).Formula = xFile.Name
Application.ActiveSheet.Cells(rowIndex, 2).Formula = xFile.Path
Application.ActiveSheet.Cells(rowIndex, 3).Formula = "Есть"
Else
Application.ActiveSheet.Cells(rowIndex, 1).Formula = xFile.Name
Application.ActiveSheet.Cells(rowIndex, 2).Formula = xFile.Path
Application.ActiveSheet.Cells(rowIndex, 3).Formula = "Нет"
rowIndex = rowIndex + 1
End If
Next xFile
If xIsSubfolders Then
For Each xSubFolder In xFolder.SubFolders
ListFilesInFolder xSubFolder.Path, True
Next xSubFolder
End If
Set xFile = Nothing
Set xFolder = Nothing
Set xFileSystemObject = Nothing
End Sub
Function GetFileOwner(ByVal xPath As String, ByVal xName As String)
Dim xFolder As Object
Dim xFolderItem As Object
Dim xShell As Object
xName = StrConv(xName, vbUnicode)
xPath = StrConv(xPath, vbUnicode)
Set xShell = CreateObject("Shell.Application")
Set xFolder = xShell.Namespace(StrConv(xPath, vbFromUnicode))
If Not xFolder Is Nothing Then
Set xFolderItem = xFolder.ParseName(StrConv(xName, vbFromUnicode))
End If
If Not xFolderItem Is Nothing Then
GetFileOwner = xFolder.GetDetailsOf(xFolderItem, 8)
Else
GetFileOwner = ""
End If
Set xShell = Nothing
Set xFolder = Nothing
Set xFolderItem = Nothing
End Function
Thank you
I'd recommend to use Option explicit but leave that to you. I tweaked your code like that
Sub ListFilesInFolder(ByVal xFolderName As String, ByVal xIsSubfolders As Boolean)
Dim xFileSystemObject As Object
Dim xFolder As Object
Dim xSubFolder As Object
Dim xFile As Object
Dim rowIndex As Long
Set xFileSystemObject = CreateObject("Scripting.FileSystemObject")
Set xFolder = xFileSystemObject.GetFolder(xFolderName)
rowIndex = Application.ActiveSheet.Range("A65536").End(xlUp).Row + 1
For Each xFile In xFolder.Files
If HasSheet(xFile.ParentFolder & "\", xFile.Name, "economy") Then
Application.ActiveSheet.Cells(rowIndex, 1).Formula = xFile.Name
Application.ActiveSheet.Cells(rowIndex, 2).Formula = xFile.path
Application.ActiveSheet.Cells(rowIndex, 3).Formula = "Sheet exists"
Else
Application.ActiveSheet.Cells(rowIndex, 1).Formula = xFile.Name
Application.ActiveSheet.Cells(rowIndex, 2).Formula = xFile.path
Application.ActiveSheet.Cells(rowIndex, 3).Formula = "Sheet does not exist"
End If
rowIndex = rowIndex + 1
Next xFile
If xIsSubfolders Then
For Each xSubFolder In xFolder.SubFolders
ListFilesInFolder xSubFolder.path, True
Next xSubFolder
End If
Set xFile = Nothing
Set xFolder = Nothing
Set xFileSystemObject = Nothing
End Sub
Note that I moved the line rowIndex = rowIndex + 1out of the ifcondition and I use another function for checking if the workbook in question contains the worksheet you are looking for. The reason is that I want to avoid to open the workbook with Workbooks.open which could lead to trouble as Auto_open code would run.
Here is the function HasSheet I used
Function HasSheet(fPath As String, fName As String, sheetName As String) As Boolean
Dim f As String
Dim res As Variant
On Error GoTo EH
f = "'" & fPath & "[" & fName & "]" & sheetName & "'!R1C1"
res = ExecuteExcel4Macro(f)
If IsError(res) Then
HasSheet = False
Else
HasSheet = True
End If
Exit Function
EH:
HasSheet = False
End Function
Function HasSheet is based on this answer
My code is as below:
Sub NewWorksheetTest()
Dim wsname As String
wsname = InputBox("Enter a name for the new worksheet")
On Error GoTo BadEntry
Sheets.Add
ActiveSheet.Name = wsname
Exit Sub
BadEntry:
MsgBox Err.Number & " :" & Err.Description, vbInformation, "There is an error...."
End Sub
My understanding is if I input a bad name (e.g. duplicate or containing ?/), there is a message explaining the reasons and at the same time the system stops a new sheet from being added.
An error msg is there but a new sheet is added.
As Tim Williams said, On Error GoTo BadEntry only works when the error appears, and sheets.add has no error so it will run normally.
This is another version you can use
vs1-no error checking
Option Compare Text
Sub NewWorksheetTest()
Dim wsname As String
wsname = InputBox("Enter a name for the new worksheet")
If Not (Checks_Sheetname (wsname)) Then Exit Sub 'check correct name
If Check_SheetExists(wsname) Then Exit Sub 'check dulicate
Sheets.Add
ActiveSheet.Name = wsname
End Sub
'https://learn.microsoft.com/en-us/office/vba/excel/concepts/workbooks-and-worksheets/name-a-worksheet-by-using-a-cell-value
Private Function Checks_Sheetname (wsname As String) As Boolean
If Len(wsname) > 31 Then Checks_Sheetname = False:exit function 'check sheetname length
Dim lst_str As Variant, item As Variant
lst_str = Array("/", "\", "[", "]", "*", "?", ":")
For Each item In lst_str
If InStr(wsname, item) > 0 Then
'...
Checks_Sheetname = False: Exit Function
End If
Next item
Checks_Sheetname = True
End Function
'https://stackoverflow.com/questions/6688131/test-or-check-if-sheet-exists
Private Function Check_SheetExists(wsname As String) As Boolean
For Each ws In Worksheets
If wsname = ws.Name Then
MsgBox ("exist")
Check_SheetExists = True
Exit Function
End If
Next ws
End Function
vs2: error checking
Sub NewWorksheetTest()
Dim wsname As String
wsname = InputBox("Enter a name for the new worksheet")
On Error GoTo BadEntry
Dim Act_wsname As String: Act_wsname = ActiveSheet.Name
ActiveSheet.Name = wsname: ActiveSheet.Name = Act_wsname 'checksyntax
Dim ws As Worksheet: Set ws = Sheets(wsname) 'check dulicate
If Not (ws Is Nothing) Then Exit Sub
Sheets.Add
ActiveSheet.Name = wsname
Exit Sub
BadEntry:
MsgBox Err.Number & " :" & Err.Description, vbInformation, "There is an error...."
End Sub
If the rename fails then you need to remove the added sheet
Sub NewWorksheetTest()
Dim wsname As String, ws As Worksheet
wsname = InputBox("Enter a name for the new worksheet")
On Error GoTo BadEntry
Set ws = Sheets.Add()
ws.Name = wsname
Exit Sub
BadEntry:
MsgBox Err.Number & " :" & Err.Description, vbInformation, "There is an error...."
If Not ws Is Nothing Then
Application.DisplayAlerts = False
ws.Delete
Application.DisplayAlerts = True
End If
End Sub
I have ContentControl drop down box in Word. Once I select an item from a Drop Down list I want to search for that in an Excel document and set the row number equal to a variable.
The code below is what I tried but the Columns("G:G").Find part says its not defined.
Sub findsomething(curRow)
Dim rng As Range
Dim rownumber As Long
curPath = ActiveDocument.path & "\"
Call Set_Variable(curPath)
StrWkShtNm = "Chapters"
If Dir(StrWkBkNm) = "" Then
MsgBox "Cannot find the designated workbook: " & StrWkBkNm, vbExclamation
Exit Sub
End If
Set rng = Columns("G:G").Find(what:=curRow)
rownumber = rng.Row
MsgBox rownumber
' Release Excel object memory
Set xlWkBk = Nothing
Set xlApp = Nothing
Application.ScreenUpdating = True
End Sub
While using more than one MS Office application it is a good idea to specify which application you are targeting:
Excel.Application.ThisWorkbook.Sheets(1).Range("A1").Select
this is what ended up working. you set me on the right track with referencing Excel.
Sub findsomething(curRow)
Dim rng As Long
Dim rownumber As Long
curPath = ActiveDocument.path & "\"
Call Set_Variable(curPath)
StrWkShtNm = "Chapters"
MsgBox "curRow = " & curRow
If Dir(StrWkBkNm) = "" Then
MsgBox "Cannot find the designated workbook: " & StrWkBkNm, vbExclamation
Exit Sub
End If
With xlApp
.Visible = False
Set xlWkBk = .Workbooks.Open(FileName:=StrWkBkNm, ReadOnly:=True, AddToMRU:=False)
With xlWkBk
With .Worksheets(StrWkShtNm)
rng = .Range("G:G").Find(what:=curRow)
MsgBox rng
End With
.Close False
End With
.Quit
End With
' Release Excel object memory
Set xlWkBk = Nothing: Set xlApp = Nothing
Application.ScreenUpdating = True
End Sub
The problem is that when I change the value in I16 or I17 I get an error. How
can I prevent this error from happening?
I check in I16 and I17 for the sheetnames, because every week an updated sheet comes available.
Thank you
Sub Compare()
Call compareSheets(range("I16").Value, range("I17").Value)
End Sub
Sub compareSheets(Sofon As String, Sofon2 As String)
Dim mycell As range
Dim mydiffs As Integer
For Each mycell In ActiveWorkbook.Worksheets(Sofon2).range("M:M")
If Not mycell.Value = ActiveWorkbook.Worksheets(Sofon).Cells(mycell.Row, mycell.Column).Value Then
mycell.Interior.Color = vbYellow
mydiffs = mydiffs + 1
End If
Next
MsgBox mydiffs & " differences found in Column M (Salesman)", vbInformation
ActiveWorkbook.Sheets(Sofon2).Select
End Sub
Just to show what I was thinking.
I agree with puzzlepiece87 that On Error is finicky, but with something this simple I would use it to avoid the excess loops.
Sub compareSheets(Sofon As String, Sofon2 As String)
Dim mycell As Range
Dim mydiffs As Integer
On Error GoTo nosheet
For Each mycell In ActiveWorkbook.Worksheets(Sofon2).Range("M:M")
If Not mycell.Value = ActiveWorkbook.Worksheets(Sofon).Cells(mycell.Row, mycell.Column).Value Then
mycell.Interior.Color = vbYellow
mydiffs = mydiffs + 1
End If
Next
MsgBox mydiffs & " differences found in Column M (Salesman)", vbInformation
ActiveWorkbook.Sheets(Sofon2).Select
Exit Sub
nosheet:
If Err.Number = 9 Then
MsgBox "One or both sheets do not exist"
Else
MsgBox Err.Description
End If
End Sub
Since the OP wanted an ISERROR type of solution, I decided to post the code which incorporates a function to check if a sheet exists in a workbook. The concept is similar to answers already posted, but it keeps any On Error statements strictly inside the function and uses regular code blocks to evaluate errors.
Sub Compare()
Dim bGo As Boolean
Dim s1 As String, s2 As String
s1 = Range("I16").Value2
s2 = Range("I17").Value2
If Not WorksheetExist(s1) Then
bGo = False
MsgBox "The sheet " & s1 & " does not exist in this workbook."
End If
If Not WorksheetExist(s2) Then
bGo = False
MsgBox "The sheet " & s2 & " does not exist in this workbook."
End If
If bGo Then compareSheets s1, s2
End Sub
Function WorksheetExist(sName As String, Optional wb As Workbook) As Boolean
Dim wbCheck As Workbook, ws As Worksheet
If wb Is Nothing Then Set wbCheck = ThisWorkbook Else: Set wbCheck = wb
On Error Resume Next
Set ws = wbCheck.Sheets(sName)
On Error GoTo 0
If Not ws Is Nothing Then WorksheetExist = True Else: WorksheetExist = False
End Function
And, based on #puzzlepiece87 methodology, here is an improved WorksheetExist Function that eliminates of On Error statements altogether.
Function WorksheetExist(sName As String, Optional wb As Workbook) As Boolean
Dim wbCheck As Workbook, ws As Worksheet
If wb Is Nothing Then Set wbCheck = ThisWorkbook Else: Set wbCheck = wb
WorksheetExist = False
For Each ws In wbCheck.Worksheets
If ws.Name = sName Then
WorksheetExist = True
Exit For
End If
Next
End Function
You could use something similar to this to call compareSheets. It will warn you if either of the two ranges do not correspond to sheet names and won't call compareSheets if true.
Dim Sheet1 As Worksheet
Dim boolI16SheetCheck As Boolean
Dim boolI17SheetCheck As Boolean
boolI16SheetCheck = False
boolI17SheetCheck = False
For Each Sheet1 in ActiveWorkbook.Worksheets
If Sheet1.Name = Activesheet.Range("I16").Value Then boolI16SheetCheck = True
If Sheet1.Name = Activesheet.Range("I17").Value Then boolI17SheetCheck = True
If boolI16SheetCheck = True And boolI17SheetCheck = True Then
Call compareSheets(range("I16").Value, range("I17").Value)
Exit Sub
End If
Next Sheet1
If boolI16SheetCheck = False Then
If boolI17SheetCheck = False Then
Msgbox "Neither I16 nor I17 sheet found."
Else
Msgbox "I16 sheet not found."
End If
Else
Msgbox "I17 sheet not found."
End If
End Sub
I have a vba code that scans a folder and its subdirectories for excel files and lists the connection strings and sql command. But my problem is my program doesn't list the inaccessible network folders that gives you the error "Access Denied." I wanna be able to list the path to the folder and indicate on the second column that the folder is inaccessible. How should I code it? I'm thinking
On Error GoTo Handler
Handler:
If Err.Number = x Then
oRng.Value = sFDR & sItem
oRng.Offset(0, 1).Value = "Inaccessible folder"
Resume Next
End If
But this code doesn't work. It doesn't specify the path of the 'access denied' folder at all. Instead, it puts the text "Inaccessible folder" to the next accessible excel file it sees.
Here's the code:
Private Const FILE_FILTER = "*.xl*"
Private Const sRootFDR = "Path" ' Root Folder
Private oFSO As Object ' For FileSystemObject
Private oRng As Range, N As Long ' Range object and Counter
Sub Main()
Application.ScreenUpdating = False
Set oFSO = CreateObject("Scripting.FileSystemObject")
N = 0
With ThisWorkbook.Worksheets("Sheet1")
.UsedRange.ClearContents ' Remove previous contents
.Range("A1:E1").Value = Array("Filename", "Connections", "Connection String", "Command Text", "Date Scanned")
Set oRng = .Range("A2") ' Initial Cell to start storing results
End With
Columns("A:E").Select
With Selection
.WrapText = True
.ColumnWidth = 45
.HorizontalAlignment = xlLeft
.VerticalAlignment = xlCenter
End With
ListFolder sRootFDR
Application.ScreenUpdating = True
Set oRng = Nothing
Set oFSO = Nothing
Columns.AutoFit
MsgBox N & " Excel files has been checked for connections."
End Sub
Private Sub ListFolder(ByVal sFDR As String)
Dim oFDR As Object
' List the files of this Directory
ListFiles sFDR, FILE_FILTER
' Recurse into each Sub Folder
On Error GoTo Handler
Handler:
If Err.Number = 5 Then
oRng.Value = sFDR & sItem
oRng.Offset(0, 1).Value = "Inaccessible folder"
Resume Next
End If
For Each oFDR In oFSO.GetFolder(sFDR).SubFolders
ListFolder oFDR.Path & "\" ' Need '\' to ensure the file filter works
Next
End Sub
Private Sub ListFiles(ByVal sFDR As String, ByVal sFilter As String)
Dim sItem As String
On Error GoTo Handler
Handler:
If Err.Number = 52 Then
oRng.Value = sFDR & sItem
oRng.Offset(0, 1).Value = "Inaccessible folder"
Resume Next
End If
sItem = Dir(sFDR & sFilter)
Do Until sItem = ""
N = N + 1 ' Increment Counter
oRng.Value = sFDR & sItem
CheckFileConnections oRng.Value ' Call Sub to Check the Connection settings
oRng.Offset(0, 4) = Now
Set oRng = oRng.Offset(1) ' Move Range object to next cell below
sItem = Dir
Loop
End Sub
Private Sub CheckFileConnections(ByVal sFile As String)
Dim oWB As Workbook, oConn As WorkbookConnection
Dim sConn As String, sCMD As String
Dim ConnectionNumber As Integer
ConnectionNumber = 1
Application.StatusBar = "Opening workbook: " & sFile
On Error Resume Next
Set oWB = Workbooks.Open(Filename:=sFile, ReadOnly:=True, UpdateLinks:=False, Password:=userpass)
If Err.Number > 0 Then
oRng.Offset(0, 1).Value = "Password protected file"
Else
With oWB
For Each oConn In .Connections
If Len(sConn) > 0 Then sConn = sConn & vbLf
If Len(sCMD) > 0 Then sCMD = sCMD & vbLf
sConn = sConn & oConn.ODBCConnection.Connection
sCMD = sCMD & oConn.ODBCConnection.CommandText
oRng.Offset(0, 1).Value = ConnectionNumber ' 1 column to right (B)
oRng.Offset(0, 2).Value = oConn.ODBCConnection.Connection ' 2 columns to right (C)
oRng.Offset(0, 3).Value = oConn.ODBCConnection.CommandText ' 3 columns to right (D)
ConnectionNumber = ConnectionNumber + 1
Set oRng = oRng.Offset(1) ' Move Range object to next cell below
Next
End With
End If
oWB.Close False ' Close without saving
Set oWB = Nothing
Application.StatusBar = False
End Sub
Hum, I tried debugging your code and found the following.
Your error handlers are coded a bit goofy. If the handler gets triggered, yet the error code is NOT the one you are testing for, then you will re-invoke the loop from start. It would be more clean to code them as:
Private Sub ListFolder(ByVal sFDR As String)
Dim oFDR As Object, lFDR As Object
' List the files of this Directory
ListFiles sFDR, FILE_FILTER
' Recurse into each Sub Folder
On Error GoTo Handler
For Each oFDR In oFSO.GetFolder(sFDR).SubFolders
ListFolder oFDR.Path & "\" ' Need '\' to ensure the file filter works
Next
Exit Sub
Handler:
If Err.Number = 70 Then
oRng.Value = sFDR
oRng.Offset(0, 1).Value = "Inaccessible folder - access denied"
End If
Resume Next
End Sub
This ensures you perform a Resume Next for all errors that trigger the handler, not just the one error you are looking for. I know for the ListFiles() sub, that re-entrance into the loop should work properly, but still it is bad form. And that code format does not work for the ListFolder() sub as it causes hard aborts.
When I changed your ListFolder as shown (and changed Err.Number checked for to 70), you code seems to work for me. I made both inaccessible files and folders, and the proper error tag was shown with the proper file names and directory names that I made inaccessible.