View Full Version : FileSystemObject and Subfolders
faberk
01-10-2007, 07:16 PM
I have a requirement to perform mass changes to the footers of .xls files. Using the filesystemobect, i have managed to display the names of the folders immediately subordinate to my main path. I would like to loop through all folders. There are many levels. How do I access the many levels below my seed path?
matthewspatrick
01-10-2007, 07:51 PM
Here is some old code of mine that will recurse through subfolders for you. Has some evidence of my old bad habits, like using the Select method for no good reason, but it works ;)
Option Explicit
' Code based on procedures found at:
' http://www.exceltip.com/show_tip/Files,_Workbook,_and_Worksheets_in_VBA/ (http://www.exceltip.com/show_tip/Files,_Workbook,_and_Worksheets_in_VBA/)
' List_files_in_a_folder_with_Microsoft_Scripting_Runtime_using_VBA_in_Micros oft_Excel/446.html
Private Declare Function SHGetPathFromIDList Lib "shell32.dll" Alias _
"SHGetPathFromIDListA" (ByVal pidl As Long, ByVal pszPath As String) As Long
Private Declare Function SHBrowseForFolder Lib "shell32.dll" Alias _
"SHBrowseForFolderA" (lpBrowseInfo As BROWSEINFO) As Long
Public Type BROWSEINFO
hOwner As Long
pidlRoot As Long
pszDisplayName As String
lpszTitle As String
ulFlags As Long
lpfn As Long
lParam As Long
iImage As Long
End Type
Private Function GetDirectory(Optional Msg) As String
Dim bInfo As BROWSEINFO
Dim path As String
Dim r As Long
Dim x As Long
Dim pos As Integer
' Root folder = Desktop
bInfo.pidlRoot = 0&
' Title in the dialog
If IsMissing(Msg) Then bInfo.lpszTitle = "Select a folder." Else bInfo.lpszTitle = Msg
' Type of directory to return
bInfo.ulFlags = &H1
' Display the dialog
x = SHBrowseForFolder(bInfo)
' Parse the result
path = Space$(512)
r = SHGetPathFromIDList(ByVal x, ByVal path)
If r Then
pos = InStr(path, Chr$(0))
GetDirectory = Left(path, pos - 1)
Else
GetDirectory = ""
End If
End Function
Sub DoListFilesInFolder()
Dim CheckPath As String
Dim Msg As Byte
Dim Drilldown As Boolean
Dim Extensions As String
CheckPath = GetDirectory()
If CheckPath = "" Then
MsgBox "No folder was selected. Procedure aborted.", vbExclamation, "StaffSmart Add-In"
Exit Sub
End If
Extensions = InputBox("Please enter the allowed file extensions (blank or all for 'all file types')", _
"Look for File Extensions", "All")
Extensions = UCase(IIf(Extensions = "", "All", Extensions))
Msg = MsgBox("Do you want to list all files in descendant folders, too?", _
vbInformation + vbYesNo, "Drill-Down")
If Msg = vbYes Then Drilldown = True Else Drilldown = False
Workbooks.Add ' create a new workbook for the file list
ActiveWindow.Zoom = 75
' add headers
With Range("A1")
.Formula = "Folder contents: "
.Font.Bold = True
.Font.Size = 12
End With
Range("A3").Formula = "Folder:"
Range("B3").Formula = "File Name:"
Range("C3").Formula = "File Size:"
Range("D3").Formula = "File Type:"
Range("E3").Formula = "Date Created:"
Range("F3").Formula = "Date Last Accessed:"
Range("G3").Formula = "Date Last Modified:"
Range("H3").Formula = "Attributes:"
Range("A3:H3").Font.Bold = True
ListFilesInFolder CheckPath, Drilldown, Extensions
' list all files included subfolders
Range("a4").Select
ActiveWindow.FreezePanes = True
Range("a3").Select
Selection.Sort Key1:=Range("A4"), Order1:=xlAscending, Key2:=Range("B4"), _
Order2:=xlAscending, Header:=xlYes
Range("H3").AddComment
With Range("H3").Comment
.Visible = False
.Text Text:="The file attribute can have any of the following values " & _
"or any logical combination of the following values:" & Chr(10) & _
"0 = Normal file. No attributes are set. " & Chr(10) & _
"1 = Read-only file" & Chr(10) & _
"2 = Hidden file" & Chr(10) & _
"4 = System file" & Chr(10) & ""
.Text Text:=Chr(10) & "8 = Disk drive volume label " & Chr(10) & _
"16 = Folder or directory " & Chr(10) & _
"32 = File changed since last backup " & Chr(10) & _
"64 = Link or shortcut " & Chr(10) & _
"128 = Compressed file" & Chr(10) & Chr(10) & _
"For example, 3 = Read-only and Hidden", Start:=200
.Shape.Height = 200
.Shape.Width = 144
End With
Range("a1") = Range("a1").Value & CheckPath & IIf(Drilldown, " (with descendants)", _
" (without descendants)")
Range("a3").Select
ActiveWindow.LargeScroll Up:=100
MsgBox "Done"
End Sub
Private Sub ListFilesInFolder(SourceFolderName As String, IncludeSubfolders As Boolean, Ext As String)
' lists information about the files in SourceFolder
' example: ListFilesInFolder "C:\FolderName\", True
Dim FSO As Object 'Scripting.FileSystemObject
Dim SourceFolder As Object 'Scripting.Folder
Dim SubFolder As Object 'Scripting.Folder
Dim FileItem As Object 'Scripting.File
Dim r As Long
Set FSO = CreateObject("Scripting.FileSystemObject") 'New Scripting.FileSystemObject
Set SourceFolder = FSO.GetFolder(SourceFolderName)
r = Range("A65536").End(xlUp).Row + 1
For Each FileItem In SourceFolder.Files
If Ext = "ALL" Or InStr(1, Ext, Mid(FileItem.Name, InStrRev(FileItem.Name, ".", -1) + 1)) > 0 Then
' display file properties
Cells(r, 1).Formula = FileItem.ParentFolder.path '& FileItem.Name
Cells(r, 2).Formula = FileItem.Name
Cells(r, 3).Formula = FileItem.Size
Cells(r, 4).Formula = FileItem.Type
Cells(r, 5).Formula = FileItem.DateCreated
Cells(r, 6).Formula = FileItem.DateLastAccessed
Cells(r, 7).Formula = FileItem.DateLastModified
Cells(r, 8).Formula = FileItem.Attributes
r = r + 1 ' next row number
End If
Next FileItem
' If "descendant" folders also get their files listed, then sub calls itself recursively
If IncludeSubfolders Then
For Each SubFolder In SourceFolder.SubFolders
ListFilesInFolder SubFolder.path, True, Ext
Next SubFolder
End If
Columns("A:H").AutoFit
Set FileItem = Nothing
Set SourceFolder = Nothing
Set FSO = Nothing
End Sub
malik641
01-10-2007, 08:09 PM
Hey faberk, Welcome to VBAX! :hi:
In addition to mathewspatrick's post (ya beat me to it, buddy :) )....
The method you're looking for is called recursive code. It's code that calls itself over and over through all subfolders and works its way from the bottom up.
The fastest method I've seen is using the Dir() function. Matt (mvidas) from this forum was kind enough to give this code here. Just call it sending the Main directory (top level) and a string array:
Function ReturnAllFilesUsingDir(ByVal vPath As String, ByRef vsArray() As String) As Boolean
Dim tempStr As String, vDirs() As String, Cnt As Long, dirCnt As Long
If Len(vsArray(0)) = 0 Then
Cnt = 0
Else
Cnt = UBound(vsArray) + 1
End If
If Right(vPath, 1) <> "\" Then vPath = vPath & "\"
On Error GoTo BadDir
tempStr = Dir(vPath, 31)
Do Until Len(tempStr) = 0
If Asc(tempStr) <> 46 Then
If GetAttr(vPath & tempStr) And vbDirectory Then
ReDim Preserve vDirs(dirCnt)
vDirs(dirCnt) = tempStr
dirCnt = dirCnt + 1
End If
BadDirGo:
End If
tempStr = Dir
SkipDir:
Loop
On Error GoTo BadFile
tempStr = Dir(vPath, 15)
Do Until Len(tempStr) = 0
ReDim Preserve vsArray(Cnt)
vsArray(Cnt) = vPath & tempStr
Cnt = Cnt + 1
tempStr = Dir
Loop
'Debug.Print Cnt
BadFileGo:
On Error GoTo 0
If dirCnt > 0 Then
For dirCnt = 0 To UBound(vDirs)
If Len(Dir(vPath & vDirs(dirCnt))) = 0 Then
ReturnAllFilesUsingDir vPath & vDirs(dirCnt), vsArray
End If
Next
End If
Exit Function
BadDir:
If tempStr = "pagefile.sys" Or tempStr = "???" Then
' Debug.Print "DIR: Skipping: " & vPath & tempStr
Resume BadDirGo
ElseIf Err.Number = 52 Then 'or err.number=5 then
' Debug.Print "No read rights: " & vPath & tempStr
Resume SkipDir
End If
Debug.Print "Error with DIR (BadDir): " & Err.Number & " - " & Err.Description
Debug.Print " vPath: " & vPath
Debug.Print " tempStr: " & tempStr
Exit Function
BadFile:
If Err.Number = 52 Then 'or err.number=5 then
' Debug.Print "No read rights: " & vPath & tempStr
Else
Debug.Print "Error with DIR (BadFile): " & Err.Number & " - " & Err.Description
Debug.Print " vPath: " & vPath
Debug.Print " tempStr: " & tempStr
End If
Resume BadFileGo
End Function
And call it like so:
Sub Test()
' How to call ReturnAllFilesUsingDir
Dim vsArray() As String
Dim vPath As String
Dim i As Long
vPath = "C:\"
Call ReturnAllFilesUsingDir(vPath, vsArray())
If Not IsError(vsArray(0)) Then
For i = 0 To UBound(vsArray())
If vsArray(i) Like "*.xls" Then
' Open the workbook and change what you need
End If
Next
End If
End Sub
This is what I use to return all files in a given directory where I have to move down all subdirectories. The IF statement "If Not IsError(vsArray(0)) Then" line still kinda bugs me because I don't think it's an accurate way to check if no files were found (although it's unlikely you would call the function with an empty directory), but it works for me like that (when no files are found). I hope you can find this equally useful for what you're trying to do :thumb
faberk
01-10-2007, 09:17 PM
Thank you gentlemen! Very interesting! :SHOCKED:
tstom
01-10-2007, 09:21 PM
Joseph. I could not get your code to run. Subscript error.
Faberk. Here is an example using the FileSystemObject. It will return all xls files from a root folder (and subfolders) as a fileobject array.
Option Explicit
Dim Files() As Object
Sub Example()
Dim Root As Object, f As Object
ReDim Files(0)
Set Root = CreateObject("Scripting.FileSystemObject").GetFolder("C:\Documents and Settings\Tom\dESKTOP")
GetFiles Root
For Each f In Root.Files
If f.Type = "Microsoft Excel Worksheet" Then
Set Files(UBound(Files)) = f
ReDim Preserve Files(UBound(Files) + 1)
End If
Next
ReDim Preserve Files(UBound(Files) - 1)
Test
End Sub
Sub Test()
Dim x As Long
For x = LBound(Files) To UBound(Files)
Debug.Print Files(x).Path
Debug.Print Files(x).DateLastModified
Debug.Print Files(x).Size
Debug.Print "-------------------------------------------------------"
Next
End Sub
Sub GetFiles(Root As Object)
Dim f As Object, sf As Object
For Each sf In Root.SubFolders
GetFiles sf
For Each f In sf.Files
If f.Type = "Microsoft Excel Worksheet" Then
Set Files(UBound(Files)) = f
ReDim Preserve Files(UBound(Files) + 1)
End If
Next
Next
End Sub
johnske
01-10-2007, 10:10 PM
Also using FileSystemObject...
Option Explicit
Sub TryThis()
ShowAllFilesAllFoldersIn ("C:\Windows\Desktop")
End Sub
Sub ShowAllFilesAllFoldersIn(FolderPath)
Dim FolderFound As Object, File As Object
For Each FolderFound In CreateObject("Scripting.FileSystemObject") _
.GetFolder(FolderPath).SubFolders
Debug.Print FolderFound.Name
If Not FolderFound.Files.Count = 0 Then
For Each File In FolderFound.Files
'do what you want with the file here
Debug.Print vbTab & File.Name
Next
End If
Next
End Sub
If you want this to apply only to .xls files, you can use somethong like this...
Sub ShowAllFilesAllFoldersIn(FolderPath)
Dim FolderFound As Object, File As Object
For Each FolderFound In CreateObject("Scripting.FileSystemObject") _
.GetFolder(FolderPath).SubFolders
Debug.Print FolderFound.Name
If Not FolderFound.Files.Count = 0 Then
For Each File In FolderFound.Files
If File.Name Like "*.xls" Then
'do what you want with the file here
Debug.Print vbTab & File.Name
End If
Next
End If
Next
End Sub
tstom
01-10-2007, 10:16 PM
Johnske. There is no recursion in your code. It will only search second level folders and no further. Also any files in the root are not accounted for...
johnske
01-10-2007, 10:26 PM
Johnske. There is no recursion in your code. It will only search second level folders and no further. Also any files in the root are not accounted for...Hmm, yeh you're right tom - just shows what happens if you do things in a rush & don't think them out properly :devil2:
tstom
01-10-2007, 10:28 PM
Well... I don't know about you but I have never been able to get a grip on recursive techniques. For some people it's easy and others it just doesn't click...
malik641
01-11-2007, 06:29 AM
Well... I don't know about you but I have never been able to get a grip on recursive techniques. For some people it's easy and others it just doesn't click...
I hear ya. I'm still having trouble with it....but I feel like I'm starting to understand it :)
Also, my apologies on the error, I forgot to add this line:
ReDim vsArray(0)
So it looks like:
Sub Test()
' How to call ReturnAllFilesUsingDir
Dim vsArray() As String
Dim vPath As String
Dim i As Long
vPath = "C:\Test Files\"
ReDim vsArray(0)
Call ReturnAllFilesUsingDir(vPath, vsArray())
If Not IsError(vsArray(0)) Then
For i = 0 To UBound(vsArray())
If vsArray(i) Like "*.xls" Then
' Open the workbook and change what you need
Debug.Print "Yes"
End If
Next
End If
End Sub
Sorry about that!
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions Inc. All rights reserved.