Get doc files from folder and subfolders using Word VBA - vba

I am inserting a bunch of Word documents into one file for post-processing. When all the files are in one folder, I got my script to work. However to make it robust for future work, I'd like to insert Word files from all folders and subfolders (and possible futher subs) from a certain starting point. I followed this Youtube tutorial: https://www.youtube.com/watch?v=zHJPliWS9FQ to consider all folders and subfolders and of course amended it for my particular use.
Sub CombineDocs()
On Error Resume Next
MsgBox "Opening"
On Error GoTo 0
Dim foldername As String 'parent folder
With Application.FileDialog(msoFileDialogFolderPicker)
.AllowMultiSelect = False
.Show
On Error Resume Next
foldername = .SelectedItems(1)
Err.Clear
On Error GoTo 0
End With
Documents.Add
Selection.Style = ActiveDocument.Styles("Heading 1")
Selection.TypeText Text:="Opening text"
Selection.TypeParagraph
Selection.InsertNewPage
Selection.InsertBreak Type:=wdSectionBreakNextPage
ActiveDocument.GoTo(What:=wdGoToPage, Count:=2).Select
Dim fso As Scripting.FileSystemObject
Dim file As Scripting.file
getfolders foldername
End sub
Sub getfolders(foldername)
Set fso = New Scripting.FileSystemObject
Call pastedoc(foldername)
Set fso = Nothing
End Sub
Sub pastedoc(StartFolderPath as String)
Dim file As Scripting.file
Dim subfol As Scripting.folder
Dim mainfolder As Scripting.folder
Set mainfolder = fso.GetFolder(StartFolderPath )
For Each file In mainfolder.Files
If ((InStr(1, LCase(fso.GetExtensionName(file.Path)), "doc", vbTextCompare) > 0) Or _
(InStr(1, LCase(fso.GetExtensionName(file.Path)), "docx", vbTextCompare) > 0)) And _
(InStr(1, file.Name, "~$") = 0) Then
Selection.InsertFile FileName:= _
file.Path _
, Range:="", ConfirmConversions:=False, Link:=False, Attachment:=False
Selection.InsertBreak Type:=wdSectionBreakNextPage
End If
Next file
For Each subfol In mainfolder.SubFolders
pastedoc subfol.Path
Next subfol
End Sub
A difference between my code and the tutorial's is that I define the parent folder in the main code and the tutorial does it in the sub script. As a result I get an
'object required'
error in the 'set mainfolder' line. I tried defining all objects and names between the main code and calling the subs but I still can't get it to work. Any guidance what could fix the code?

One option: assuming the End Sub for CombineDocs was after the getfolders call, you can:
Remove getfolders entirely
In CombineDocs, say pastedoc foldername instead of getfolders foldername
Change the beginning of pastedoc to:
Sub pastedoc(StartFolderPath as String)
Dim fso As Scripting.FileSystemObject ' ** Added
Set fso = New Scripting.FileSystemObject ' ** Added
Dim file As Scripting.file
Dim subfol As Scripting.folder
Dim mainfolder As Scripting.folder
Set mainfolder = fso.GetFolder(StartFolderPath )
' ... (everything else the same)
In general, you need to Dim variables either in the Sub where they are used, or at the top of your module, outside any subs. Please put the Dims inside the Subs whenever you can, since that makes your code much easier to change and maintain.

Related

How to make this code apply recursively to all sub-folders

I found this code to change .doc files to .docx files. I would like to modify it so I can specify a top level folder and have it work through it AND every sub-folder. Appreciate any assistance.
Sub TranslateDocIntoDocx()
Dim objWordApplication As New Word.Application
Dim objWordDocument As Word.Document
Dim strFile As String
Dim strFolder As String
strFolder = "C:\Temp\doc\"
strFile = Dir(strFolder & "*.doc", vbNormal)
While strFile <> ""
With objWordApplication
Set objWordDocument = .Documents.Open(FileName:=strFolder & strFile, AddToRecentFiles:=False, ReadOnly:=True, Visible:=False)
With objWordDocument
.SaveAs FileName:=strFolder & Replace(strFile, "doc", "docx"), FileFormat:=16
.Close
End With
End With
strFile = Dir()
Wend
Set objWordDocument = Nothing
Set objWordApplication = Nothing
End Sub
I suggest switching from Dir to FileSystemObject. With FSO, you can get a folder as an object with GetFolder and then access the Folder Object's files and folders as collections. This enables For Each loops like For Each File In Folder. and then you can do the recursion For Each SubFolder In Folder where you can re-call the macro as if each subfolder was the top level folder.
Sub TranslateDocIntoDocx()
Dim objWordApplication As New Word.Application
Dim strFolder As String
strFolder = "C:\Temp\doc\"
Dim StartingFolder As Object
Set StartingFolder = CreateObject("Scripting.FileSystemObject").GetFolder(strFolder)
FolderToDocx StartingFolder, objWordApplication
Set objWordApplication = Nothing
End Sub
Sub FolderToDocx(Folder As Object, wdApp As Word.Application)
Dim File As Object
For Each File In Folder.Files
If LCase(Split(File.Name, ".")(1)) = "doc" Then SaveToDocx File, wdApp
Next
Dim SubFolder As Object
For Each SubFolder In Folder.Subfolders
FolderToDocx SubFolder, wdApp
Next
End Sub
Sub SaveToDocx(File As Object, wdApp As Word.Application)
With wdApp.Documents.Open(File.Path, AddToRecentFiles:=False, ReadOnly:=True, Visible:=False)
.SaveAs Filename:=File.Path & "x"), FileFormat:=16
.Close
End With
End Sub
The file matching expression I did in this answer is just an example. You may want to improve that expression to prevent errors. One error that may come up is with Microsoft Office temp files. They are usually hidden and prefixed with ~$ like ~$Word Document.docx. So to avoid accidentally matching one of those, it would be good to exclude anything with that prefix.
I would recommend splitting out the file searching into a separate function: it's easier to tweak your logic and the main method doesn't get overloaded by the code for finding the files.
Sub TranslateDocIntoDocx()
Dim objWordApplication As New Word.Application
Dim objWordDocument As Word.Document
Dim colFiles As Collection
Dim strFile
Set colFiles = GetMatchingFiles("C:\Temp\doc\", "*.doc")
For Each strFile In colFiles
With objWordApplication
Set objWordDocument = .Documents.Open(Filename:=strFile, _
AddToRecentFiles:=False, ReadOnly:=True, Visible:=False)
With objWordDocument
.SaveAs Filename:=strFile & "x", FileFormat:=16
.Close
End With
End With
Next strFile
End Sub
'Search beginning at supplied folder root, including subfolders, for
' files matching the supplied pattern. Return all matches in a Collection
Function GetMatchingFiles(startPath As String, filePattern As String) As Collection 'of paths
Dim colFolders As New Collection, colFiles As New Collection
Dim fso As Object, fldr, subfldr, fl
Set fso = CreateObject("scripting.filesystemobject")
colFolders.Add startPath 'queue up root folder for processing
Do While colFolders.Count > 0 'loop until the queue is empty
fldr = colFolders(1) 'get next folder from queue
colFolders.Remove 1 'remove current folder from queue
With fso.getfolder(fldr)
For Each fl In .Files
If UCase(fl.Name) Like UCase(filePattern) Then 'check pattern
colFiles.Add fl.Path 'collect the full path
End If
Next fl
For Each subfldr In .subFolders
colFolders.Add subfldr.Path 'queue any subfolders
Next subfldr
End With
Loop
Set GetMatchingFiles = colFiles
End Function

VBA- filter unecessary folders

I have a question about reading files within folders. I found this code:
sub sample()
Dim FileSystem As Object
Dim HostFolder As String
HostFolder = "C:\"
Set FileSystem = CreateObject("Scripting.FileSystemObject")
DoFolder FileSystem.GetFolder(HostFolder)
end sub
Sub DoFolder(Folder)
Dim SubFolder
For Each SubFolder In Folder.SubFolders
DoFolder SubFolder
Next
Dim File
For Each File In Folder.Files
' Operate on each file
Next
End Sub
But how would you go about avoiding a specific folder which is within the original one?
Let's say the you have a folder A which in turn has several folder Bs. within these folders, there are the file required but also another folder, always with the same name, let's say C.
How would you filter out folders Cs?
Thank you for your time
You can try something like this:
' List of complete path of files in folder / subfolders
' Needs to add "Microsoft Scripting Runtime" reference to your file
Sub FolderFilesPath(ByVal pFolder As String, ByRef pColFiles As Collection, _
Optional ByVal pGetSubFolders As Boolean, Optional ByVal pFilter As Collection)
Dim sFolder As String
Dim oFSO As New FileSystemObject
Dim oFolder, oSubFolder As Folder
Dim oFile As File
sFolder = IIf(Right(pFolder, 1) <> "\", pFolder & "\", pFolder)
Set oFolder = oFSO.GetFolder(sFolder)
If Not ExistsInCollection(pFilter, sFolder) Then
For Each oFile In oFolder.Files
pColFiles.Add oFile
Next oFile
If pGetSubFolders Then
For Each oSubFolder In oFolder.SubFolders
FolderFilesPath oSubFolder.Path, pColFiles, pGetSubFolders, pFilter
Next
End If
End If
End Sub
' Vba collection contains
Function ExistsInCollection(col As Collection, key As Variant) As Boolean
On Error GoTo err
ExistsInCollection = True
IsObject (col.Item(key))
Exit Function
err:
ExistsInCollection = False
End Function
'------------------------------------------------------------------------------
Sub TestMe()
Dim colFiles As New Collection, sFilePath As Variant
Dim colExcludedFolders As New Collection
Dim sHostFolder As String
sHostFolder = "C:\temp"
With colExcludedFolders
' add folders you want to exclude
.Add sHostFolder & "\C\", sHostFolder & "\C\"
End With
FolderFilesPath ThisWorkbook.Path, colFiles, True, colExcludedFolders
' colFiles contains filtered files
For Each sFilePath In colFiles
With sFilePath
' do what you want with filtered files
Debug.Print .Path & " - " & .Name & " - " & .DateCreated
End With
Next sFilePath
End Sub

Copy and past data from all files in a folder to master file in same folder

I am trying to copy a specific range of data from SAME tab d. Rate Card in 3 different worksheets named "RCR Schedule C - Rate Card.xls" in one folder and paste in master file named "RFP consolidation macro".
I created the loop and am able to copy/paste from the 1st file in the folder but not the other 2. Below is the code for that. Is there any way to ensure the code works for all the files in the folder and not only the first one?
Private Sub CommandButton2_Click()
Dim MyFile As String
Dim erow
MyFile = Dir("c:\Users\s4043091\Desktop\New folder\RFP\NEW\")
Do While Len(MyFile) > 0
If MyFile = "RFP consolidation macro.xlsm" Then
Exit Sub
End If
'Workbooks.Open ("c:\Users\s4043091\Desktop\New folder\RFP\NEW\RCR Schedule C - Rate Card.xls")
Workbooks("RCR Schedule C - Rate Card.xls").Worksheets("d. Rate Card").Range("b3:ah482").Copy _
Workbooks("RFP consolidation macro.xlsm").Worksheets("Masterfile-Rate Card").Range("b1")
Workbooks("RCR Schedule C - Rate Card.xls").Worksheets("a. Company Background").Range("e7").Copy _
Workbooks("RFP consolidation macro.xlsm").Worksheets("Masterfile-Rate Card").Range("a4:a482")
'Range("A2:D200").Copy
'ActiveWorkbook.Close
erow = Sheet1.Cells(Rows.Count, "a").End(xlUp).Offset(1, 0).Row
'ActiveSheet.Paste Destination:=Worksheets("Macro").Range(Cells(erow, 1), Cells(erow, 1))
MyFile = Dir
Loop
End Sub
Not tested, but this should get you going:
Option Explicit
Public Sub LoopFilesInFolderEarlyFSO()
'Early Bound - requires Reference to Microsoft Scripting Runtime; with the reference there is Intellisense
'the Early Bound part:
Dim FSO As FileSystemObject
Set FSO = New FileSystemObject
'Everything after is the same:
Dim myFolderPath As String
myFolderPath = "C:\thePath"
If FSO.FolderExists(myFolderPath) Then
Dim myFolder As Folder
Set myFolder = FSO.GetFolder(myfoderpath)
Else
GoTo ExitSub
End If
Dim currFile As File
For Each currFile In myFolder.Files
Debug.Print currFile.Name
Next
ExitSub:
End Sub
Public Sub LoopFilesInFolderLateFSO()
'Late Bound - requires Creating the FSO; without the reference there is no Intellisense
'the Late Bound part:
Dim FSO As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
'Everything after is the same:
Dim myFolderPath As String
myFolderPath = "C:\thePath"
If FSO.FolderExists(myFolderPath) Then
Dim myFolder As Folder
Set myFolder = FSO.GetFolder(myfoderpath)
Else
GoTo ExitSub
End If
Dim currFile As File
For Each currFile In myFolder.Files
Debug.Print currFile.Name
Next
ExitSub:
End Sub

How do i create a VB Macro that will save a certain file to all sub folders in a particular directory?

This is what I have so far, might be good might not haha!
I have been trying to save a word document to about 400+ folders without having to go through them all, can this be done through VB Macros? I got it working to just save it to the directory, but I cannot save it to all the Subfolders.
Dim FileSystem As Object
Dim HostFolder As String
Sub DoFolder(folder)
HostFolder = ("H:\test2")
Set FileSystem = CreateObject("Scripting.FileSystemObject")
DoFolder FileSystem.GetFolder(HostFolder)
Dim SubFolder
For Each SubFolder In folder.SubFolders
DoFolder SubFolder
Next
Dim File
For Each File In folder.Files
Set FileSystem = CreateObject("Scripting.FileSystemObject")
' Operate on each file
ActiveDocument.Save
Next
End Sub
I recommended reading: Chip Pearson -Recursion And The FileSystemObject
Make a recursive subroutine to iterate over all the subfolders (and their subfolders) in the root directory.
getAllSubfolderPaths: returns an array that lists all the sub folders in a folder.
Function getAllSubfolderPaths(FolderPath As String, Optional FSO As Object, Optional List As Object)
Dim fld As Object
If FSO Is Nothing Then
Set FSO = CreateObject("Scripting.Filesystemobject")
Set List = CreateObject("SYstem.Collections.ArrayList")
End If
List.Add FolderPath
For Each fld In FSO.GetFolder(FolderPath).SubFolders
getAllSubfolderPaths fld.Path, FSO, List
Next
getAllSubfolderPaths = List.ToArray
End Function
Test
Sub Test()
Const RootFolder As String = "C:\Users\Owner\Pictures"
Const SourcePath As String = "C:\Users\Owner\Documents\Youcam"
Const SourceFileName As String = "Capture.PNG"
Dim fld As Variant, FolderArray As Variant
Dim Destination As String, Source As String
FolderArray = getAllSubfolderPaths(RootFolder)
For Each fld In FolderArray
Destination = fld & "\" & SourceFileName
Source = SourcePath & "\" & SourceFileName
'Delete old copy of file
If Destination <> Source And Len(Dir(Destination)) Then Kill Destination
VBA.FileCopy Source:=Source, Destination:=Destination
Next
End Sub
Gotta love auditing requirements... You're basically on the right path, but you really only need one FileSystemObject. About the only errors I see are that you need the .Path of the folder here...
For Each SubFolder In folder.SubFolders
DoFolder SubFolder.Path '<---Here.
Next
...and you don't need to loop through all the files here (you may be overthinking this one a bit):
For Each File In folder.Files
Set FileSystem = CreateObject("Scripting.FileSystemObject")
' Operate on each file
ActiveDocument.Save
Next
Also, I'd suggest using early binding instead of late binding (although the example below can easily be switched). I'd do something a bit more like this:
Private Sub SaveDocToAllSubfolders(targetPath As String, doc As Document, _
Optional root As Boolean = False)
With New Scripting.FileSystemObject
Dim current As Scripting.folder
Set current = .GetFolder(targetPath)
If Not root Then
doc.SaveAs .BuildPath(targetPath, doc.Name)
End If
Dim subDir As Scripting.folder
For Each subDir In current.SubFolders
SaveDocToAllSubfolders subDir.Path, doc
Next
End With
End Sub
The root flag is just whether or not to save a copy in the host folder. Call it like this:
SaveDocToAllSubfolders "H:\test2", ActiveDocument, True

VBA search for a specific subfolder in many folders and move all the files in it

can you help me?
i want a macro vba that search for a SPECIFIC subfolder for example (Xfolder) between all the folders and subfolders that exist and move their files.
P:\Desktop\Folder1\subfolder\SUBFOLDER1\Xfolder
I'm using the VBA Scripting Runtime objects
Set oSourceFolder = fso.GetFolder(source)
If Dir(destinationFolder, 16) = "" Then MkDir (destinationFolder)
For Each oFile In oFolder.Files
If Dir(destinationFolder,16) = "" Then
fso.MoveFile oFile.Path, destinationFolder
End If
Next oFile
fso.DeleteFolder oFolder.Path
Next oFolder
Here's a solution:
Dim fsoFileSystem As New FileSystemObject
Dim foFolder As Folder, foSubFolder As Folder
Dim fFile As File
Dim strStartFolder As String, strMoveFolder As String, strTargetFolder As String
strStartFolder = "\\A\B\C"
strMoveFolder = "SearchFolder"
strTargetFolder = "\\B\D\E"
Set foFolder = fsoFileSystem.GetFolder(strStartFolder)
For Each foSubFolder In foFolder.SubFolders
If foSubFolder.Name = strMoveFolder Then
For Each fFile In foSubFolder.Files
fsoFileSystem.MoveFile fFile, strTargetFolder & "\"
Next
End If
Next
strStartFolder is the folder to Screen for subfolders.
strMoveFolder is the name of the Folder to look for.
strTargetFolder is the Folder to where all the strMoveFolder's files shall be moved.
To found some folder use something like this
Sub findFolder()
Dim searchFolderName As String
searchFolderName = "somePath"
Dim FileSystem As Object
Set FileSystem = CreateObject("Scripting.FileSystemObject")
doFolder FileSystem.getFolder(searchFolderName)
End Sub
Sub doFolder(Folder)
Dim subFolder
On Error Resume Next
For Each subFolder In Folder.subfolders
If Split(subFolder, "\")(UBound(Split(subFolder, "\"))) = "testFolder" Then
MsgBox "gotcha"
End
End If
doFolder subFolder
Next subFolder
End Sub
And then you can do whatever with that folder and its content. So with i little use of google (one maybe two words) you can achieve what you wana