This is driving me mad: I have a sub and a function in a powerpoint vba.
The sub starts by allowing me to select a dir. The function, called from the sub, finds a file in the dir. I want it as a function outside of the sub, as I will need to use it multiple times.
The sub is still under development, so doesn't do much, but works. The function works too if I give it something to do - like open the found file (ie uncomment that line in my code below) - but I can't for the life of me get it to return the filePath to the sub. Please help!
The sub:
Sub ManagementSummaryMerge()
Dim folderPath As String
'select dir
Dim FldrPicker As FileDialog
Set pptApp = CreateObject("PowerPoint.Application")
pptApp.Visible = True
'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
folderPath = .SelectedItems(1) & "\"
End With
'In Case of Cancel
NextCode:
folderPath = folderPath
If folderPath = "" Then GoTo EndOfSub
'set _Main <= string I want to look for
Dim v As String
v = "_Main"
Dim fullFilePathIWantToSet As String
'set value of fullFilePathIWantToSet from findFile function
fullFilePathIWantToSet = findFile(folderPath, v)
'when I test, this MsgBox appears, but blank
MsgBox fullFilePathIWantToSet
'If I can get this working properly, I want to be able to do something like this:
'objFSO.CopyFile fullFilePathIWantToSet, duplicateFilePath
'Presentations.Open (duplicateFilePath)
'numSlides = ActivePresentation.Slides.Count
'etc
EndOfSub:
'let the sub end
End Sub
The function:
Function findFile(ByRef folderPath As String, ByVal v As String) As String
Dim fileName As String
Dim fullFilePath As String
Dim duplicateFilePath As String
Dim numFolders As Long
Dim numSlides As Integer
Dim folders() As String
Dim i As Long
If Right(folderPath, 1) <> "\" Then folderPath = folderPath & "\"
ileName = Dir(folderPath & "*.*", vbDirectory)
While Len(fileName) <> 0
If Left(fileName, 1) <> "." Then
fullFilePath = folderPath & fileName
duplicateFilePath = folderPath & "duplicate " & fileName
If (GetAttr(fullFilePath) And vbDirectory) = vbDirectory Then
ReDim Preserve folders(0 To numFolders) As String
folders(numFolders) = fullFilePath
numFolders = numFolders + 1
Else
'if true, the it matches the string we are looking for
If InStr(10, fullFilePath, v) > 0 Then
'if true, then it isn't in a dir called P/previous, which I want to avoid
If InStr(1, fullFilePath, "evious") < 1 Then
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set f = objFSO.GetFile(fullFilePath)
'If true, then it isn't one of those funny duplicate files that microsoft makes, that has the ~ at the beginning of the file name
If f.Size > 5000 Then GoTo ReturnSettings
' if we're here then we have found the one single file that we want! Go ahead and do our thing
findFile = fullFilePath
Exit Function
End If
End If
End If
End If
End If
fileName = Dir()
Wend
For i = 0 To numFolders - 1
findFile folders(i), v
Next i
End Function
I'm a total VBA noob, so have just pva glued this together from what I can find online. Is it not working because of the findFile loop returning an array of one instead of a string? I thought the 'Exit Function' call would do away with that issue.
Please excuse the recursive if statements - the people that I am doing this for don't have a totally standard way of storing their ppts, but this hones down on the ppt I want. When the sub is complete, it will itself loop through 130 sub dirs of the selected dir, and within each of those sub dirs it will grab various slides from six different ppts and merge them into one, ie consolidate data from 780 ppts into 130 - something I definitely want to automate!
This is my first question posted on stack Overflow, so I hope I have posed it clearly and correctly. I have searched extensively for a solution to this. I hope the solution pops out to you! Many thanks in advance.
This is a classic case of needing to use Option Explicit.
You have a missing f from filename and this goes unchecked as a variable ilename not filename.
You should put Option Explicit at the top of every module and declare all your variables. There is also a missing label for a GoTo statement which I have added.
Note: You are doing a full string case sensitive match on the file name within the selected folder.
Option Explicit
Sub ManagementSummaryMerge()
Dim folderPath As String, FldrPicker As FileDialog, pptApp As Object
Set pptApp = CreateObject("PowerPoint.Application")
pptApp.Visible = True
Set FldrPicker = Application.FileDialog(msoFileDialogFolderPicker)
With FldrPicker
.Title = "Select A Target Folder"
.AllowMultiSelect = False
If .Show <> -1 Then GoTo NextCode
folderPath = .SelectedItems(1) & "\"
End With
'In Case of Cancel
NextCode:
folderPath = folderPath
If folderPath = "" Then GoTo EndOfSub
'set _Main <= string I want to look for
Dim v As String
v = "_Main"
Dim fullFilePathIWantToSet As String
'set value of fullFilePathIWantToSet from findFile function
fullFilePathIWantToSet = findFile(folderPath, v)
'when I test, this MsgBox appears, but blank
MsgBox fullFilePathIWantToSet
'If I can get this working properly, I want to be able to do something like this:
'objFSO.CopyFile fullFilePathIWantToSet, duplicateFilePath
'Presentations.Open (duplicateFilePath)
'numSlides = ActivePresentation.Slides.Count
'etc
EndOfSub:
'let the sub end
End Sub
Function findFile(ByRef folderPath As String, ByVal v As String) As String
Dim fileName As String
Dim fullFilePath As String
Dim duplicateFilePath As String
Dim numFolders As Long
Dim numSlides As Integer
Dim folders() As String, i As Long
If Right(folderPath, 1) <> "\" Then folderPath = folderPath & "\"
fileName = Dir(folderPath & "*.*", vbDirectory)
While Len(fileName) <> 0
If Left(fileName, 1) <> "." Then
fullFilePath = folderPath & fileName
duplicateFilePath = folderPath & "duplicate " & fileName
If (GetAttr(fullFilePath) And vbDirectory) = vbDirectory Then
ReDim Preserve folders(0 To numFolders) As String
folders(numFolders) = fullFilePath
numFolders = numFolders + 1
Else
'if true, the it matches the string we are looking for
If InStr(10, fullFilePath, v) > 0 Then
'if true, then it isn't in a dir called P/previous, which I want to avoid
If InStr(1, fullFilePath, "evious") < 1 Then
Dim objFSO As Object, f As Object
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set f = objFSO.GetFile(fullFilePath)
'If true, then it isn't one of those funny duplicate files that microsoft makes, that has the ~ at the beginning of the file name
If f.Size > 5000 Then GoTo ReturnSettings
' if we're here then we have found the one single file that we want! Go ahead and do our thing
findFile = fullFilePath
Exit Function
End If
End If
End If
End If
fileName = Dir()
Wend
For i = 0 To numFolders - 1
findFile folders(i), v
Next i
Exit Function
ReturnSettings:
End Function
OK, I have a solution to this. It's not totally elegant, because it relies on globally set variables, but it works and is good enough for me:
' show if a mistake is made
Option Explicit
' globally set the var we want to return to the sub from the function
Public foundFilePath As String
Sub FindIt()
Dim colFiles As New Collection, vFile As Variant, mypath As String
FldrPicker As FileDialog, fileToFind As String, pptApp As Object
Set pptApp = CreateObject("PowerPoint.Application")
pptApp.Visible = True
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
NextCode:
mypath = mypath
If mypath = "" Then GoTo EndOf
'
' find file
'
fileToFind = "*your_string_here*"
'calls to function RecursiveDir, which sets first matching file as foundFilePath
Call RecursiveDir(colFiles, mypath, fileToFind, True)
' do what you want with foundFilePath
MsgBox "Path of file found: " & foundFilePath
'
'find second file
'
fileToFind = "*your_second_string_here*"
Call RecursiveDir(colFiles, mypath, fileToFind, True)
MsgBox "Second file path: " & foundFilePath
EndOf:
End Sub
Public Function RecursiveDir(colFiles As Collection, _
strFolder As String, _
strFileSpec As String, _
bIncludeSubfolders As Boolean)
Dim strTemp As String, fullFilePath As String
Dim colFolders As New Collection
Dim vFolderName As Variant
'Add files in strFolder matching strFileSpec to colFiles
strFolder = TrailingSlash(strFolder)
strTemp = Dir(strFolder & strFileSpec)
Do While strTemp <> vbNullString
strFileSpec = Replace(strFileSpec, "*", "")
If InStr(strTemp, strFileSpec) > 0 Then
foundFilePath = strFolder & strTemp
Exit Function
End If
colFiles.Add strFolder & strTemp
strTemp = Dir
Loop
If bIncludeSubfolders Then
'Fill colFolders with list of subdirectories of strFolder
strTemp = Dir(strFolder, vbDirectory)
Do While strTemp <> vbNullString
If (strTemp <> ".") And (strTemp <> "..") Then
If (GetAttr(strFolder & strTemp) And vbDirectory) <> 0 Then
colFolders.Add strTemp
End If
End If
strTemp = Dir
Loop
'Call RecursiveDir for each subfolder in colFolders
For Each vFolderName In colFolders
Call RecursiveDir(colFiles, strFolder & vFolderName, strFileSpec, True)
Next vFolderName
End If
End Function
Public Function TrailingSlash(strFolder As String) As String
If Len(strFolder) > 0 Then
If Right(strFolder, 1) = "\" Then
TrailingSlash = strFolder
Else
TrailingSlash = strFolder & "\"
End If
End If
End Function
That works. What was a better solution for me is the below. It uses separate subs / functions to do the following: pick a folder ; loop through first-child folders ; recursively search for a file, using a partial file name, in all folders and subfolders ; do something with the found file/s (plural if the search function is called on multiple strings).
It's not necessary to separate out like this, but I find it easier for separation of concerns and keeping things simple.
Sub 1: Root folder picker. Passes selected folder onto sub 2
Option Explicit
Public foundFilePath As String
Sub StartSub()
' selects the parent folder and passes it to LoopSuppliers
Dim masterPath As String, FldrPicker As FileDialog, pptApp As Object
Set pptApp = CreateObject("PowerPoint.Application")
Set FldrPicker = Application.FileDialog(msoFileDialogFolderPicker)
pptApp.Visible = True
With FldrPicker
.Title = "Select A Target Folder"
.AllowMultiSelect = False
If .Show <> -1 Then GoTo NextCode
masterPath = .SelectedItems(1) & "\"
End With
NextCode:
masterPath = masterPath
If masterPath = "" Then GoTo EndOf
Call LoopSuppliers(masterPath) ' goes to masterFolder in LoopSuppliers sub
EndOf:
End Sub
Sub two: simply loops through the parent folder and passes the path of each first-child sub folder to function three to do something with it. Adapted from here.
Private Sub LoopSuppliers(masterFolder As String)
Dim objFSO As Object, objFolder As Object, objSupplierFolder As Object
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(masterFolder)
For Each objSupplierFolder In objFolder.SubFolders
'objSupplierFolder.path objSubFolder.Name <- object keys I can grab
Call ManipulateFiles(objSupplierFolder.path)
Next objSupplierFolder
End Sub
Function 1: Grabs file paths for doing something with
Private Function ManipulateFiles(ByRef FolderPath As String)
Dim file1 As String, file2 As String, file3 As String
' each of these calls find a file anywhere in a suppliers subfolders, using the second param as a search string, and then holds it as a new var
Call FindSupplierFile(FolderPath, "search_string1")
file1 = foundFilePath
Call FindSupplierFile(FolderPath, "search_string2")
file2 = foundFilePath
Call FindSupplierFile(FolderPath, "search_string3")
file3 = foundFilePath
'
' do something with the files!
'
End Function
Function 2: This is the function that takes a dir, a search string, and then loops through all the dirs folders and sub folders until it gets a match. I've included extra filtering, to show how I further narrowed down the files that could be returned to function 1.
Private Function FindSupplierFile(ByRef FolderPath As String, ByVal v As String) As String
Dim FileName As String, fullFilePath As String, numFolders As Long, Folders() As String, i As Long
Dim objFSO As Object, f As Object
If Right(FolderPath, 1) <> "\" Then FolderPath = FolderPath & "\"
FileName = Dir(FolderPath & "*.*", vbDirectory)
While Len(FileName) <> 0
If Left(FileName, 1) <> "." Then
fullFilePath = FolderPath & FileName
If (GetAttr(fullFilePath) And vbDirectory) = vbDirectory Then
ReDim Preserve Folders(0 To numFolders) As String
Folders(numFolders) = fullFilePath
numFolders = numFolders + 1
Else
'
' my filters
'
If InStr(1, fullFilePath, "evious") < 1 Then ' filter out files in folders called "_p/Previous"
If InStr(10, fullFilePath, v) > 0 Then ' match for our search string 'v'
Set objFSO = CreateObject("Scripting.FileSystemObject") ''
Set f = objFSO.GetFile(fullFilePath) '' use these three code lines to check that the file is more that 5kb - ie not a tiny ~ file
''
If f.Size > 5000 Then ''
foundFilePath = fullFilePath ' if we get in here we have the file that we want
Exit Function ' as we have found the file we want we can exit the function (which means we carry on with ManipulateFiles)
End If ' end f.size
End If ' end InStr v if
End If ' end InStr evious if
'
' end of my filters
'
End If ' end get attr if else
End If ' end left if
FileName = Dir()
Wend ' while len <> 0
For i = 0 To numFolders - 1
FindSupplierFile Folders(i), v
Next i
End Function
Related
How can I make the second set of code reference the selection made in the first set, instead of the hard coded location it currently uses? these two sets do exactly what I like and ultimately I want to combine them, but just just cant figure out how to make the second set use the path from the first.. Ive searched for days, and tried everything I could think of. Its got to be 1 simple thing I've overlooked.
Sub SelectFolder()
Dim sFolder As String
' Open the select folder prompt
With Application.FileDialog(msoFileDialogFolderPicker)
If .Show = -1 Then ' if OK is pressed
sFolder = .SelectedItems(1)
End If
End With
If sFolder <> "" Then ' if a file was chosen
' *********************
' put your code in here
' *********************
End If
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub EveryPresentationInFolder()'Performs some operation on every
'presentation file in a folder adapted from PPTools.com
Dim sFolder As String ' Full path to folder we'll examine
Dim sFileSpec As String ' Filespec, e.g. *.PNG
Dim sFileName As String ' Name of a file in the folder
Dim oPres As Presentation
Dim lngSld As Long
Dim rayNum() As String
Dim sngL As Single
Dim sngT As Single
Dim sngW As Single
Dim opic As Shape
sFolder = Environ("USERPROFILE") & "\Desktop\Images\" ' This is where I want the folder ive picked
sFileSpec = "*.PNG"
Set oPres = ActivePresentation
sngL = 0
sngT = 0.6 * 28.3465
sngW = oPres.PageSetup.SlideWidth
sFileName = Dir$(sFolder & sFileSpec)
While sFileName <> ""
Debug.Print sFileName
rayNum = Split(sFileName, ".")
lngSld = CLng(rayNum(0))
If lngSld <= oPres.Slides.Count Then
Set opic = oPres.Slides(lngSld).Shapes.AddPicture(FileName:=sFolder & sFileName, _
LinkToFile:=False, _
SaveWithDocument:=True, _
Left:=sngL, _
Top:=sngT, _
Width:=sngW)
opic.LockAspectRatio = True
opic.Width = sngW
opic.Left = 0
opic.Top = sngT
opic.ZOrder (msoSendToBack)
End If
sFileName = Dir()
Wend
End Sub
I took the advice and tried to make the new info work for me. I know its just an ordering of the elements, and I feel like im close, but I don't understand the the problem. why wont this work?
Function SelectFolder() As String
Dim sFolder As String
' Open the select folder prompt
With Application.FileDialog(msoFileDialogFolderPicker)
If .Show = -1 Then ' if OK is pressed
sFolder = .SelectedItems(1)
End If
End With
If sFolder <> "" Then ' if a file was chosen
SelectFolder = sFolder
Else
SelectFolder = ""
End If
Dim sFileSpec As String ' Filespec, e.g. *.PNG
Dim sFileName As String ' Name of a file in the folder
Dim oPres As Presentation
Dim lngSld As Long
Dim rayNum() As String
Dim sngL As Single
Dim sngT As Single
Dim sngW As Single
Dim opic As Shape
sFolder = SelectFolder ' This is where I want the folder ive picked
sFileSpec = "*.jpg"
Set oPres = ActivePresentation
sngL = 0
sngT = 0.6 * 28.3465
sngW = oPres.PageSetup.SlideWidth
sFileName = Dir$(sFolder & sFileSpec)
While sFileName <> ""
Debug.Print sFileName
rayNum = Split(sFileName, ".")
lngSld = CLng(rayNum(0))
If lngSld <= oPres.Slides.Count Then
Set opic = oPres.Slides(lngSld).Shapes.AddPicture(FileName:=sFolder & sFileName, _
LinkToFile:=False, _
SaveWithDocument:=True, _
Left:=sngL, _
Top:=sngT, _
Width:=sngW)
opic.LockAspectRatio = True
opic.Width = sngW
opic.Left = 0
opic.Top = sngT
opic.ZOrder (msoSendToBack)
End If
sFileName = Dir()
Wend
End Function
Re "How do I call the location instead of sFolder=Environ"
Change it to this:
sFolder = SelectFolder
Then change Sub SelectFolder to a Function instead:
Function SelectFolder() as String
Dim sFolder As String
' Open the select folder prompt
With Application.FileDialog(msoFileDialogFolderPicker)
If .Show = -1 Then ' if OK is pressed
sFolder = .SelectedItems(1)
End If
End With
If sFolder <> "" Then ' if a file was chosen
SelectFolder = sFolder
' *********************
' put your code in here
' *********************
Else
SelectFolder = ""
End If
End Function
I am trying to load a listbox with files in a folder through MFDialogbox Picker.
Unfortunately, its not working. Request your help.
Below is the code, I am using. Thanks
I find that 'mypath' variable is holding the correct name of the folder that was selected.
But, I find nothing is working thereafter. Please help.
Private Sub UserForm_Initialize()
Dim myfiles As String, mypath As String
Dim fileList() As String
Dim fName As String
Dim fPath As String
Dim I As Integer
With Application.FileDialog(msoFileDialogFolderPicker)
.AllowMultiSelect = False
If .Show = False Then Exit Sub
mypath = .SelectedItems(1)
DoEvents
End With
MsgBox mypath
If Right(mypath, 1) <> "\" Then mypath = mypath & "\"
ReDim fileList(1 To I)
fName = Dir(mypath)
MsgBox fName
While fName <> ""
'add fName to the list
I = I + 1
ReDim Preserve fileList(1 To I)
fileList(I) = fName
'get next filename
fName = Dir()
Wend
'see if any files were found
If I = 0 Then
MsgBox "No files found"
Exit Sub
End If
'cycle through the list and add to listbox
For I = 1 To UBound(fileList)
Me.ListBox1.AddItem fileList(I)
Next
End Sub
The problem is that I is 0 (as it is not set) when reaching ReDim fileList(1 To I). If you replace it with ReDim fileList(1 To 1), it should work.
I was working with a vba and I'm trying to open all excel files in a folder (about 8-10) based on cell values. I was wondering if this is the right approach to opening it, it keeps giving me syntax error where I wrote the directory. and when I rewrote that section, the vba only shot out the msgbox which meant it had to have looped and did something right? but didn't open any files. Any information will help. Thank you guys so much for taking the time to help me in any way.
Sub OpenFiles()
Dim search As Worksheet
Dim customer As Range
Dim customerfolder As Range
Dim QualityHUB As Workbook
'setting variable references
Set QualityHUB = ThisWorkbook
Set search = Worksheets("Search")
Set customer = Worksheets("Search").Range("$D$1")
Set customerfolder = Worksheets("Search").Range("$D$3")
With QualityHUB
If IsEmpty((customer)) And IsEmpty((customerfolder)) Then
MsgBox "Please Fill out Customer Information and search again"
Exit Sub
End If
End With
With QualityHUB
Dim MyFolder As String
Dim MyFile As String
Dim Directory As String
Directory = "O:\LAYOUT DATA\" & customer & "\" & customerfolder"
MyFile = Dir(Directory & "*.xlsx")
Do While MyFile <> ""
Workbooks.Open Filename:=MyFile
MyFile = Dir()
Loop
MsgBox "Files Open for " + customerfolder + " complete"
End With
End Sub
This worked for me perfectly
Sub OpenFiles()
Dim search As Worksheet
Dim customer As Range
Dim customerfolder As Range
Dim QualityHUB As Workbook
'setting variable references
Set QualityHUB = ThisWorkbook
Set search = Worksheets("Search")
Set customer = Worksheets("Search").Range("$D$1")
Set customerfolder = Worksheets("Search").Range("$D$3")
With QualityHUB
If IsEmpty((customer)) And IsEmpty((customerfolder)) Then
MsgBox "Please Fill out Customer Information and search again"
Exit Sub
End If
End With
With QualityHUB
Dim MyFolder As String
Dim MyFile As String
Dim Directory As String
Directory = "O:\LAYOUT DATA\" & customer & "\" & customerfolder & "\"
MyFile = Dir(Directory & "*.xlsx")
Do While MyFile <> ""
Workbooks.Open Filename:=Directory & MyFile
MyFile = Dir()
Loop
MsgBox "Files Open for " + customerfolder + " complete"
End With
End Sub
one of the issue was, you had to write
Workbooks.Open Filename:=Directory & MyFile
instead of
Workbooks.Open Filename:=MyFile
Corrected some issues with your code and cleaned it up, give this a try. I think the big issue was you had an extra double-quote, and you missing the ending \ in the Directory line:
Sub OpenFiles()
Dim QualityHUB As Workbook
Dim search As Worksheet
Dim customer As String
Dim customerfolder As String
Dim Directory As String
Dim MyFile As String
'setting variable references
Set QualityHUB = ThisWorkbook
Set search = QualityHUB.Worksheets("Search")
customer = search.Range("$D$1").Value
customerfolder = search.Range("$D$3").Value
If Len(Trim(customer)) = 0 Or Len(Trim(customerfolder)) = 0 Then
MsgBox "Please Fill out Customer Information and search again"
Exit Sub
End If
Directory = "O:\LAYOUT DATA\" & customer & "\" & customerfolder & "\" '<--- This requires the ending \
MyFile = Dir(Directory & "*.xlsx")
Do While Len(MyFile) > 0
Workbooks.Open Filename:=Directory & MyFile
MyFile = Dir()
Loop
MsgBox "Files Open for " + customerfolder + " complete"
End Sub
I found this code online and it will open all the excel files in a folder, you can adapt the code to apply a function to the workbook, once it is open.
Option Explicit
Type FoundFileInfo
sPath As String
sName As String
End Type
Sub find()
Dim iFilesNum As Integer
Dim iCount As Integer
Dim recMyFiles() As FoundFileInfo
Dim blFilesFound As Boolean
blFilesFound = FindFiles("G:\LOCATION OF FOLDER HERE\", _
recMyFiles, iFilesNum, "*.xlsx", True)
End Sub
Function FindFiles(ByVal sPath As String, _
ByRef recFoundFiles() As FoundFileInfo, _
ByRef iFilesFound As Integer, _
Optional ByVal sFileSpec As String = "*.*", _
Optional ByVal blIncludeSubFolders As Boolean = False) As Boolean
Dim iCount As Integer '* Multipurpose counter
Dim sFileName As String '* Found file name
Dim wbResults, file, WS_Count, i, gcell, col, finRow, wbCodeBook As Workbook, lCount, name, looper
Dim WorksheetExists
Set wbCodeBook = ThisWorkbook
'*
'* FileSystem objects
Dim oFileSystem As Object, _
oParentFolder As Object, _
oFolder As Object, _
oFile As Object
Set oFileSystem = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
Set oParentFolder = oFileSystem.GetFolder(sPath)
If oParentFolder Is Nothing Then
FindFiles = False
On Error GoTo 0
Set oParentFolder = Nothing
Set oFileSystem = Nothing
Exit Function
End If
sPath = IIf(Right(sPath, 1) = "\", sPath, sPath & "\")
'*
'* Find files
sFileName = Dir(sPath & sFileSpec, vbNormal)
If sFileName <> "" Then
For Each oFile In oParentFolder.Files
If LCase(oFile.name) Like LCase(sFileSpec) Then
iCount = UBound(recFoundFiles)
iCount = iCount + 1
ReDim Preserve recFoundFiles(1 To iCount)
file = sPath & oFile.name
name = oFile.name
End If
On Error GoTo nextfile:
Set wbResults = Workbooks.Open(Filename:=file, UpdateLinks:=0)
''insert your code here
wbResults.Close SaveChanges:=False
nextfile:
Next oFile
Set oFile = Nothing '* Although it is nothing
End If
If blIncludeSubFolders Then
'*
'* Select next sub-forbers
For Each oFolder In oParentFolder.SubFolders
FindFiles oFolder.path, recFoundFiles, iFilesFound, sFileSpec, blIncludeSubFolders
Next
End If
FindFiles = UBound(recFoundFiles) > 0
iFilesFound = UBound(recFoundFiles)
On Error GoTo 0
'*
'* Clean-up
Set oFolder = Nothing '* Although it is nothing
Set oParentFolder = Nothing
Set oFileSystem = Nothing
End Function
Function SSCGetColumnCodeFromIndex(colIndex As Variant) As String
Dim tstr As String
Dim prefixInt As Integer
Dim suffixInt As Integer
prefixInt = Int(colIndex / 26)
suffixInt = colIndex Mod 26
If prefixInt = 0 Then
tstr = ""
Else
prefixInt = prefixInt - 1
tstr = Chr(65 + prefixInt)
End If
tstr = tstr + Chr(65 + suffixInt)
SSCGetColumnCodeFromIndex = tstr
End Function
Function GetColNum(oSheet As Worksheet, name As String)
Dim Endrow_Col, i
'For loop to get the column number of name
Endrow_Col = oSheet.Range("A1").End(xlToRight).Column
oSheet.Select
oSheet.Range("A1").Select
For i = 0 To Endrow_Col - 1 Step 1
If ActiveCell.Value <> name Then
ActiveCell.Offset(0, 1).Select
ElseIf ActiveCell.Value = name Then
GetColNum = ActiveCell.Column
Exit For
End If
Next i
End Function
Function ShDel(name As String)
End If
End Function
I am creating an vba-access application with a drop down box Combo_History that gives the user the ability to launch a .pdf file from a sub-folder within a main folder called "Scanned Work Orders (Archives)". What I am trying to do is use a certain number called an "M" number(M number because every number starts with an M ex: M765196) to find this file without using a specific sub folder here is what i have so far:
Dim fso, oFolder, oSubfolder, oFile, queue As Collection
Set fso = CreateObject("Scripting.FileSystemObject")
Set queue = New Collection
queue.Add fso.GetFolder("T:\Scanned Work Orders (Archives)")
Do While queue.Count > 0
Set oFolder = queue(1)
queue.Remove 1 'dequeue
If oFile = Combo_History.Value Then
Application.FollowHyperlink ("T:\Scanned Work Orders (Archives)" & oFile)
End If
For Each oSubfolder In oFolder.SubFolders
queue.Add oSubfolder 'enqueue
Next oSubfolder
For Each oFile In oFolder.Files
If oFile = Combo_History.Value Then
Application.FollowHyperlink ("T:\Scanned Work Orders (Archives)" & oFile)
End If
Next oFile
Loop
The problem is it gets stuck in an infinite loop because it cannot find the .pdf with the keyword name M765196 even though it is in that folder. Is there something im missing? Or an easier way to find the .pdf file?
I'm adding a second answer here because solving for a wildcard differed more than I anticipated from the original.
Searching for files using a wildcard isn't difficult, but it comes with some implications, such as returning a list of results instead of a single result. In addition, I fortunately ran into a permissions error on one of my subfolders which caused me to think about how to handle that situation.
Option Explicit
Private recurseDepth As Integer
Sub test()
Dim rootFolder As String
Dim filename As String
Dim resultFiles() As String
Dim i As Integer
rootFolder = "C:\Temp"
filename = "*.pdf"
If FindFiles(rootFolder, filename, resultFiles) > 0 Then
For i = 1 To UBound(resultFiles)
Debug.Print Format(i, "00") & ": " & resultFiles(i)
Next i
Else
Debug.Print "No files found!"
End If
End Sub
Public Function FindFiles(thisFolder As String, filespec As String, _
ByRef fileList() As String) As Integer
'--- starts in the given folder and checks all files against the filespec.
' the filespec MAY HAVE A WILDCARD specified, so the function returns
' an array of full pathnames (strings) to each file that matches
' Parameters: thisFolder - string containing a full path to the root
' folder for the search
' filespec - string containing a single filename to
' search for, --or--
' string containing a wildcard string of
' files to search for
' (result==>)fileList - an array of strings, each will be a full
' path to a file matching the input filespec
' Returns: (integer) count of the files found that match the filespec
On Error GoTo Error_FindFile
Static fso As Object
Static pathCollection As Collection
Dim fullFilePath As String
Dim oFile As Object
Dim oFolder As Object
Dim oSubfolder As Object
'--- first time through, set up the working objects
If recurseDepth = 0 Then
Set fso = CreateObject("Scripting.FileSystemObject")
Set pathCollection = New Collection
End If
recurseDepth = recurseDepth + 1
'--- focus on the given folder
Set oFolder = fso.GetFolder(thisFolder)
'--- first test if we have permissions to access the folder and
' if there are any files in the folder
On Error Resume Next
If oFolder.Files.Count > 0 Then
If Err.Number = 0 Then
'--- loop through all items in the folder. some are files and
' some are folders -- use recursion to search the subfolders
For Each oFile In oFolder.Files
If oFile.Name Like filespec Then
pathCollection.Add oFolder.Path & "\" & oFile.Name
End If
Next oFile
For Each oSubfolder In oFolder.SubFolders
FindFiles oSubfolder.Path, filespec, fileList
Next oSubfolder
Else
'--- if we get here it's usually a permissions error, so
' just skip this folder
Err.Clear
End If
End If
On Error GoTo Error_FindFile
Exit_FindFile:
recurseDepth = recurseDepth - 1
If (recurseDepth = 0) And (pathCollection.Count > 0) Then
'--- pull the paths out of the collection and make an array, because most
' programs uses arrays more easily
ReDim fileList(1 To pathCollection.Count)
Dim i As Integer
For i = 1 To pathCollection.Count
fileList(i) = pathCollection.Item(i)
Next i
End If
FindFiles = pathCollection.Count
Exit Function
Error_FindFile:
Debug.Print "Error (" & Err.Number & "): " & Err.Description & _
" on " & oSubfolder.Path
GoTo Exit_FindFile
End Function
Your loop setup didn't lend itself very well to recursion in looking for the file. The code below should work for you.
Also, you're using late-binding for your FileSystemObjects - which is perfectly fine. But the way you have them declared causes them all to be evaluated as Variants. It may be a pain, but it's better to break out each variable Dim on as separate line and to exactly specify what type it should be.
Option Explicit
Sub test()
Dim fso As Object
Dim rootFolder As String
Dim filename As String
Dim fullpath As String
Set fso = CreateObject("Scripting.FileSystemObject")
rootFolder = "C:\Users\user\Documents"
filename = "testfile.txt"
fullpath = FindFile(fso, rootFolder, filename)
Debug.Print "file is ";
If Len(fullpath) > 0 Then
Debug.Print "FOUND! : " & fullpath
Else
Debug.Print "NOT found. Go look for it yourself!"
End If
End Sub
Function FindFile(fso As Object, thisFolder As String, filename As String) As String
On Error GoTo Error_FindFile
Dim fullFilePath As String
Dim oFolder As Object
Dim oSubfolder As Object
Set oFolder = fso.GetFolder(thisFolder)
'--- first check if the file is in the current folder
fullFilePath = oFolder.Path & "\" & filename
If fso.FileExists(fullFilePath) Then
'--- we're done, nothing more to do here
Else
'--- the file isn't in this folder, so check for any subfolders and search there
fullFilePath = ""
For Each oSubfolder In oFolder.SubFolders
Debug.Print "looking in " & oSubfolder.Path
If FindFile(fso, oSubfolder.Path, filename) <> "" Then
'--- found the file, so return the full path
fullFilePath = oSubfolder.Path & "\" & filename
Exit For
End If
Next oSubfolder
End If
Exit_FindFile:
FindFile = fullFilePath
Exit Function
Error_FindFile:
'--- we'll probably get mostly permission errors, so just skip (or log, or print out)
' the permission error and move on
If Err.Number = 70 Then
Debug.Print "Permission error on " & oSubfolder.Path
End If
GoTo Exit_FindFile
End Function
This page suggests the following technique for finding a wildcard recursively:
Sub Macro1()
Dim colFiles As New Collection
RecursiveDir colFiles, "C:\Photos\", "*.jpg", True
Dim vFile As Variant
For Each vFile In colFiles
Debug.Print vFile
Next vFile
End Sub
Public Function RecursiveDir(colFiles As Collection, _
strFolder As String, _
strFileSpec As String, _
bIncludeSubfolders As Boolean)
Dim strTemp As String
Dim colFolders As New Collection
Dim vFolderName As Variant
'Add files in strFolder matching strFileSpec to colFiles
strFolder = TrailingSlash(strFolder)
strTemp = Dir(strFolder & strFileSpec)
Do While strTemp <> vbNullString
colFiles.Add strFolder & strTemp
strTemp = Dir
Loop
If bIncludeSubfolders Then
'Fill colFolders with list of subdirectories of strFolder
strTemp = Dir(strFolder, vbDirectory)
Do While strTemp <> vbNullString
If (strTemp <> ".") And (strTemp <> "..") Then
If (GetAttr(strFolder & strTemp) And vbDirectory) <> 0 Then
colFolders.Add strTemp
End If
End If
strTemp = Dir
Loop
'Call RecursiveDir for each subfolder in colFolders
For Each vFolderName In colFolders
Call RecursiveDir(colFiles, strFolder & vFolderName, strFileSpec, True)
Next vFolderName
End If
End Function
Public Function TrailingSlash(strFolder As String) As String
If Len(strFolder) > 0 Then
If Right(strFolder, 1) = "\" Then
TrailingSlash = strFolder
Else
TrailingSlash = strFolder & "\"
End If
End If
End Function
I'd like to contribute to PeterT's solution (second answer)! It appears I don't have enough points to comment, so I'm posting this as an answer.
I tested the solution and it works, but it has some (minor) bugs! I didn't test it on a server with complicated privileges, but I'll eventually have to do that in the near future!
If the startFolder is empty (no files, but subfolders) the function doesn't continue to search in startFolders' subfolders.
Search for A*.pdf and a*.PDF will not give the same result. Given the fact that Windows file system is case insensitive, it's wise to have case insensitive search. Perhaps it won't work on MAC?!
In addition, I added two (optional) extra parameters, code for garbage collection and early binding for FSO objects (I prefer that!):
boolean subFolders - if false the function will not search beyond the
startFolder
boolean fullPath - if false the function will return only file names without the path; useful (at least to me) especially if subFolders=false.
After the search finishes (recurseDepth = 0) all objects are set to Nothing.
Here is the code:
Public Function FindFiles( _
ByVal startFolder As String, _
ByVal fileSpec As String, _
ByRef fileList() As String, _
Optional ByVal subFolders As Boolean = True, _
Optional ByVal fullPath As Boolean = True) _
As Long
'--- starts in the given folder and checks all files against the filespec.
' the filespec MAY HAVE A WILDCARD specified, so the function returns
' an array of files with or withour full pathnames (strings) to each file that matches
' Parameters: startFolder - string containing a full path to the root
' folder for the search
' fileSpec - string containing a single filename to
' search for, --or--
' string containing a wildcard string of
' files to search for
' (result==>)fileList - an array of strings, each will be a full
' path to a file matching the input filespec
' subFolders - include subfolders in startFolder
' fullPath - true=>fullFile path; false=>fileName only
' Returns: (integer) count of the files found that match the filespec
Dim fullFilePath As String
Dim Path As String
Static fso As FileSystemObject
Static pathCollection As Collection
Dim oFile As file
Dim oFolder As Folder
Dim oSubfolder As Folder
On Error GoTo Error_FindFile
'--- first time through, set up the working objects
If recurseDepth = 0 Then
Set fso = New FileSystemObject ' CreateObject("Scripting.FileSystemObject")
Set pathCollection = New Collection
End If
recurseDepth = recurseDepth + 1
'--- focus on the given folder
Set oFolder = fso.GetFolder(startFolder)
'--- first test if we have permissions to access the folder and
' if there are any files in the folder
On Error Resume Next
If oFolder.files.Count > 0 Or oFolder.subFolders.Count > 0 Then
If Err.Number = 0 Then
'--- loop through all items in the folder. some are files and
' some are folders -- use recursion to search the subfolders
If fullPath Then
Path = oFolder.Path & "\"
Else
Path = ""
End If
For Each oFile In oFolder.files
' If oFile.name Like fileSpec Then
If LCase(oFile.name) Like LCase(fileSpec) Then
pathCollection.Add Path & oFile.name
End If
Next oFile
If subFolders Then
For Each oSubfolder In oFolder.subFolders
FindFiles oSubfolder.Path, fileSpec, fileList, subFolders, fullPath
Next oSubfolder
End If
Else
'--- if we get here it's usually a permissions error, so
' just skip this folder
Err.Clear
End If
End If
On Error GoTo Error_FindFile
Exit_FindFile:
recurseDepth = recurseDepth - 1
If (recurseDepth = 0) Then
If (pathCollection.Count > 0) Then
'--- pull the paths out of the collection and make an array, because most
' programs uses arrays more easily
ReDim fileList(1 To pathCollection.Count)
Dim i As Integer
For i = 1 To pathCollection.Count
fileList(i) = pathCollection.Item(i)
Next i
End If
FindFiles = pathCollection.Count
Set fso = Nothing
Set pathCollection = Nothing
Set oFile = Nothing
Set oFolder = Nothing
Set oSubfolder = Nothing
End If
Exit Function
Error_FindFile:
Debug.Print "Error (" & Err.Number & "): " & Err.Description & _
" on " & oSubfolder.Path
GoTo Exit_FindFile
End Function
I had a large number of doc files that I wanted to convert to docx files.
I discovered that there was not a really good way to automatically do this conversion.
I have submitted the method I used to do this but perhaps there are now other ways.
I found a few thing that might help:
Microsoft Bulk Converter
Simple Microsoft Word macro
However I was not satisfied with macro provided. I needed something recursive to also convert nested files. So I expanded it to do so.
Sub SaveAllAsDOCX()
'Search #EXT to change the extensions to save to docx
Dim strDocName As String
Dim strPath As String
Dim oDoc As Document
Dim fDialog As FileDialog
Dim intPos As Integer
'Create a folder dialog
Set fDialog = Application.FileDialog(msoFileDialogFolderPicker)
With fDialog
.Title = "Select root folder and click OK"
.AllowMultiSelect = False
.InitialView = msoFileDialogViewList
If .Show <> -1 Then
MsgBox "Cancelled By User", , "List Folder Contents"
Exit Sub
End If
'Select root folder
strPath = fDialog.SelectedItems.Item(1)
'Ensure the Folder Name ends with a "\"
If Right(strPath, 1) <> "\" Then strPath = strPath + "\"
End With
'Close any open documents
If Documents.Count > 0 Then
Documents.Close SaveChanges:=wdPromptToSaveChanges
End If
'remove any quotes from the folder string
If Left(strPath, 1) = Chr(34) Then
strPath = Mid(strPath, 2, Len(strPath) - 2)
End If
'begin recusion
recurse (strPath)
End Sub
'This method controls the recusion
Function recurse(folder As String)
'save all the files in the current folder
SaveFilesInFolder (folder)
'get all the subfolders of the current folder
Dim folderArray
folderArray = GetSubFolders(folder)
'Loop through all the non-empty elements for folders
For j = 1 To UBound(folderArray)
If folderArray(j) <> "" Then
'begin recusion on subfolder
recurse (folder & folderArray(j) & "\")
End If
Next
End Function
'Saves all files with listed extensions
Function SaveFilesInFolder(folder As String)
'List of extensions to look for #EXT
Dim strFilename As String
extsArray = Array("*.rtf", "*.doc")
'Loop through extensions
For i = 0 To (UBound(extsArray))
'select the 1st file with the current extension
strFilename = Dir(folder & extsArray(i), vbNormal)
'double check the current extension (don't to resave docx files)
Dim ext As String
ext = ""
On Error Resume Next
ext = Right(strFilename, 5)
If ext = ".docx" Or ext = "" Then
'Don't need to resave files in docx format
Else
'Save the current file in docx format
While Len(strFilename) <> 0
Set oDoc = Documents.Open(folder & strFilename)
strDocName = ActiveDocument.FullName
intPos = InStrRev(strDocName, ".")
strDocName = Left(strDocName, intPos - 1)
strDocName = strDocName & ".docx"
oDoc.SaveAs FileName:=strDocName, _
FileFormat:=wdFormatDocumentDefault
oDoc.Close SaveChanges:=wdDoNotSaveChanges
strFilename = Dir
Wend
End If
Next
strFilename = ""
End Function
'List all the subfolders in the current folder
Function GetSubFolders(RootPath As String)
Dim FS As New FileSystemObject
Dim FSfolder As folder
Dim subfolder As Variant
Set FSfolder = FS.GetFolder(RootPath)
'subfolders is variable length
Dim subfolders() As String
ReDim subfolders(1 To 10)
Dim i As Integer
i = LBound(subfolders)
For Each subfolder In FSfolder.subfolders
subfolders(i) = subfolder.Name
'increase the size of subfolders if it's needed
i = i + 1
If (i >= UBound(subfolders)) Then
ReDim subfolders(1 To (i + 10))
End If
Next subfolder
Set FSfolder = Nothing
GetSubFolders = subfolders
End Function
Yeah I know it's a lot of code. :)