Loop Through Folders - Apply Macro to All Files - vba

I am trying to loop through folders using VBA code Found here: Loop Through All Subfolders Using VBA
The code I have simply copied and added my own macro. Although it does not give an error. The code is not working. It simply does not respond when I run the Macro.
I have 500+ files I need to apply a macro to - some of the files are really deeply nested within folders.
I really would appreciate some one to help me create a working Macro.
The ones found on stack exchange - either give an error or simply don't respond.
If any one has a macro enabled working version of the code submitted here - it would be a great help.
I am well aware that there are VBA excel looping through folders code, there is also a recursive one - when I test these solutions they do not work for me. That is not to say that they dont work at all.
Pointing me to other threads is not helping me -Ive read all the threads.
Ive spent time testing them.
This is What I need:
- Find file of Certain type ie docx in Folder > SubFolder > Sub Folder
- Apply my own macro to it
Version 1 Found on Stack exchange:
Sub NewFolder()
Dim FileSystem As Object
Dim HostFolder As String
HostFolder = "C:\Users\Shana\Desktop 2\Folder1\"
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
ActiveDocument.Range.Text = "Replaced"
' Operate on each file
Next
End Sub
I have looked through the other VBA Loop though folders code. I have been unable to get them to work.
The code when I create macro in the VBA editor simply does not work.
Version 2 Found on stack exchange:
Function GetFilesIn(Folder As String) As Collection
Dim F As String
Set GetFilesIn = New Collection
F = Dir(Folder & "\*")
Do While F <> ""
GetFilesIn.Add F
F = Dir
Loop
End Function
Function GetFoldersIn(Folder As String) As Collection
Dim F As String
Set GetFoldersIn = New Collection
F = Dir(Folder & "\*", vbDirectory)
Do While F <> ""
If GetAttr(Folder & "\" & F) And vbDirectory Then GetFoldersIn.Add F
F = Dir
Loop
End Function
Sub Test()
Dim C As Collection, F
Debug.Print
Debug.Print "Files in C:\"
Set C = GetFilesIn("C:\Users\Shana\Desktop 2\Folder1\")
For Each F In C
Debug.Print F
Next F
Debug.Print
Debug.Print "Folders in C:\"
Set C = GetFoldersIn("C:\Users\Shana\Desktop 2\Folder1\")
For Each F In C
ActiveDocument.Range.Text = "Replaced"
'Debug.Print F
Next F
End Sub
The above also does not work - am I doing something wrong?
This is What I need:
- Find file of Certain type ie docx in Folder > SubFolder > Sub Folder
- Apply my own macro to it
Please do not mark as duplicate as I need a working version of VBA code to cycle through all my docx files.

if any one still needs a solution to this. VBA to Loop through directory and sub folders.
After I was unable to make the code found here work for what ever reason.
I have found this VBA add in from -
Graham Mayor
http://www.gmayor.com/document_batch_processes.htm
The only catch is you need to call the functions and not Macros.
The macros need to be converted to functions.
Thanks to Graham for the Great Add in for all those needing to batch process documents in a directory - full of deeply nested sub folders!

Related

VBA For Excel (CSV), Looping through files to get row, then appending rows into one List

I am having trouble coding this VBA Macro for a bunch of CSV files (10000). After searching I found/used this for my code:
Loop through files in a folder using VBA? . It doesn't seem to work and I'm not sure why... I have tried the While loop but it is very slow I don't know if it can finish running.
Sub LoopThroughFiles()
Dim MyObj As Object, MySource As Object, file As Variant
file = Dir("C:\Users\me\Desktop\test")
While (file <> "")
If InStr(file, "test") > 0 Then
'// my macro code is here
Exit Sub
End If
file = Dir
Wend
End Sub
What else should I try changing? Where did I go wrong? I have also tried using this code https://www.thespreadsheetguru.com/the-code-vault/2014/4/23/loop-through-all-excel-files-in-a-given-folder but am unsure what else to change besides the directory and the 'Change First Worksheet's Background Fill Blue.
Also tried this http://www.ozgrid.com/VBA/loop-through.htm which seems pretty fool proof but I cant get it to work...
UPDATES FROM L8N
Option Explicit
Sub looper()
Dim fso As Scripting.FileSystemObject
Dim aFolder As Scripting.Folder
Dim aFile As Scripting.file
Dim aText As Scripting.TextStreame
Dim singleLine As String
Set fso = New FileSystemObject
Set aFolder = fso.GetFolder("C:\Users\ME\Desktop\test") 'set path to the folder that contains the files
For Each aFile In aFolder.Files 'loops through every file in the top level of the folder
If InStr(1, vbBinaryCompare) > 0 Then
Range("A2:D200210").Clear 'what i want to happen to every file
Set aText = fso.OpenTextFile(aFile.Path, ForReading)
Do Until aText.AtEndOfStream
singleLine = aText.ReadLine 'read line into string, every call advances the line counter by one, this prevents skipping lines
If InStr(1, singleLine, vbBinaryCompare) > 0 Then Debug.Print singleLine ' in line case, prints line if target value is found
Loop
End If
Next aFile
Debug.Print "finished"
End Sub
It runs, but it does not seem to implement the changes I want (Range("A2:D200210").Clear ) to each file. Also the string name for my code does not matter, the info in the sheet does not either. My original code was to test if it looped at all.
I don't know exactly what you are trying to do, the code you have does the following:
file = Dir("C:\Users\me\Desktop\test") writes the filename to file if the file "test" exists, if you use Dir("C:\Users\me\Desktop\test\") the function will return the name of the first file it finds.
On subsequent runs it will return the next file in the folder, keep in mind that this is a global call, so if you call the function somewhere else it may interfere. Using the Microsoft Scripting Engine Runtime is preferable in most cases apart from quick checks if a file exists.
If InStr(file, "test") > 0 Then You test if "test" is a part of the filename, so far so good, but keep in mind to tell InStr how it should compare the two strings. InStr accepts four parameters (all of them optional), be sure to pass the proper ones. The microsoft documentation is actually quite decent.
Is this what you wanted? I think you might be looking for something inside the .csv file, if so I can extend the script below.
A simple way to loop though all files in a folder is attached below:
Option Explicit
Sub looper()
Dim fso As Scripting.FileSystemObject
Dim aFolder As Scripting.Folder
Dim aFile As Scripting.file
Dim aText As Scripting.TextStream
Dim targetName As String 'string that identifies files
Dim targetWord As String 'string that identifies line inside csv file
Dim singleLine As String
Set fso = New FileSystemObject
Set aFolder = fso.GetFolder("C:\Users\Me\Desktop\test") 'set folder that contains the files
targetName = "someFileName"
targetWord = "someString"
For Each aFile In aFolder.Files 'loops through every file in the top level of the folder
If InStr(1, aFile.Name, targetName, vbBinaryCompare) > 0 Then
Debug.Print "Found a matching File: "; aFile.Name
Set aText = fso.OpenTextFile(aFile.Path, ForReading)
Do Until aText.AtEndOfStream
singleLine = aText.ReadLine 'read line into string, every call advances the line counter by one, this prevents skipping lines
If InStr(1, singleLine, targetWord, vbBinaryCompare) > 0 Then Debug.Print singleLine ' in line case, prints line if targer value is found
Loop
End If
Next aFile
Debug.Print "finished"
End Sub
Bonus Info:
Use option explicit to make sure all variables are declared properly
Edit:
Not able to add comments to your post yet, so I'll put the response here.
If InStr(1, vbBinaryCompare) > 0 Then this line is now broken as it will always return 0. If you want to loop through every file just omit the IF-Contitional or set it to If True Then.
Range("A2:D200210").Clear is a so called implicit reference, the Range Object refers to the "Global" Worksheet. Every time this piece of code is executed, the change happens on the "Global" Worksheet, a nice answer by Mathieu Guindon from just recently explains this.
It runs, but it does not seem to implement the changes I want (Range("A2:D200210").Clear ) to each file. Also the string name for my code does not matter, the info in the sheet does not either. My original code was to test if it looped at all.
So from what I can see you try to delete everything but the first row inside a .csv file. A .csv file is not a worksheet(even though you can import it into excel), so you can't use the Range property.
Fortunately, there is an even easier way to do this, just use the Microsoft Scripting Runtime to edit the .csv file.
Set aText = aFile.OpenAsTextStream(ForReading) ' open file in read mode
singleLine = aText.ReadLine ' read the first line and store it
Set aText = aFile.OpenAsTextStream(ForWriting) ' open file in write mode
aText.Write (singleLine) 'write the line you saved before
Or even more compact:
aFile.OpenAsTextStream(ForWriting).Write aFile.OpenAsTextStream(ForReading).ReadLine 'overwrites the file with what was written in the first line.
The advantage with the longer code is the ability to use the string somewhere else, for example storing it somewhere in your workbook.

Word macro working on every document

I have written a macro in Word and it works every time I open ANY of Word documents. I don't know what is the reason for such an action and I'd like to ask you why?
Private Sub Document_Open()
Dim fso As Object
Dim f As Object
Dim plik As Object
Dim sciezka As String
ActiveDocument.Content.Select
Selection.Delete
ActiveDocument.Select
sciezka = Application.ActiveDocument.Path & "\"
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.GetFolder(Application.ActiveDocument.Path)
For Each plik In f.Files
If Not Left(Right(plik.Name, 8), 4) = "rozp" And Right(plik.Name, 4) = ".pdf" Then
Selection.TypeText plik.Name
Selection.TypeParagraph
End If
Next plik
End Sub
You probably stored it in normal.dotm document This code will apply to all documents. You should move it and store it in module that is in the document your are dealing with.
Edit 1: You created a .docm document but are you sure you stored your code in the docm document and not in the normal.dotm document?
Edit 2: I created a word test.docm document and put your code in a new module (under project(Test) -> Modules -> Module 1). Then I started the code and it create 1 paragraph per files in the same folder (giving they respect the check in your IF condition). Then, I transfered that code in Project(Test) -> Microsoft Word Objects -> ThisDocument; so it works at opening. It does exactly the same as intended at the opening.
Finally, I tested opening other word documents (docx and docm) in the same folder and the code did not run.
I cannot reproduce your problem running the exact same code. This reinforce the idea that you wrote your code under Normal -> Microsoft Word Object -> ThisDocument while in the VBA frame.
Have a good day,
Jonathan.

VBA Verify File extension as excel file?

I run this vba which goes through folders and pulls data which it compiles together in one big sheet. My issue is I was getting errors for hidden files called thumbs.db and I need to add something so that it verifies that it is only pulling files with xlsx extensions. Below is the code I am using.
Sub DoFolder(Folder)
Dim SubFolder As Folder
Dim i As Integer
Dim CopyR As Range
For Each SubFolder In Folder.SubFolders
DoFolder SubFolder
Next
If Folder.SubFolders.Count = 0 Then
If Folder.Files.Count = 1 Then
If Mid(Folder.Files, Len(Folder.Files) - 3, 4) = "xlsx" Then
Else: MsgBox "2+ files: " & Folder.Path
End If
End If
For Each File In Folder.Files
Hoover File
Next
Else
End If
End Sub
The line I am having issues with figuring out is
If Mid(Folder.Files, Len(Folder.Files) - 3, 4) = "xlsx" Then
Any help on this would be really appreciated
Folder.Files is a collection not a string.
Recursive File Search:
Sub DoFolder(FolderName As String, Optional fso As Object)
Dim f As Object, MySubFolder As Object, RootFolder As Object
Dim cFiles As Collection
If fso Is Nothing Then Set fso = CreateObject("Scripting.FileSystemObject")
Set RootFolder = fso.GetFolder(FolderName)
For Each MySubFolder In RootFolder.SubFolders
DoFolder MySubFolder.Path, fso
Next
Set cFiles = New Collection
For Each f In RootFolder.Files
If f.Name Like "*xls*" Then cFiles.Add f
Next
If cFiles.Count > 0 Then
MsgBox cFiles.Count & " files found in " & RootFolder.Name
For Each f In cFiles
Hoover f
Next
End If
End Sub
A quick solution is simply to check for xlsx being contained in the name of the file. Like this:
If InStr(1,"FileName","xlsx",vbTextCompare)<1 then
Thus, you would be in the safe side, unless someone renames thumbs.db to thumbsxlsx.db.
Assuming you're using the FileSystemObject, which it looks like you are even though we can't see the declarations, and assuming you're only wanting to call Hoover for .xlsx files you can use the following code
If Right(File.Name, 4) = "xlsx" Then
Hoover File
End If
As a further improvement to the answer by user6432984.. FSO does have a function to obtain the file extension, but the function is not part of the File object, but is the fso.GetExtensionName()
You would expect that the File.Type property could be used, but that gives the application name associated with that file extension - not very useful.
If f.Type Like "*xls*" Then cFiles.Add f
However the FSO-based function works as follows:
For Each f In RootFolder.Files
If fso.GetExtensionName(f.Path) Like "*xls*" Then cFiles.Add f
Next

Select values from a range to the right of a given cell and iteratively create a subfolder from them

Hope you are well.
I'm trying to create a mass folder creator using Excel and vba. It's my first time using VBA as I usually focus on web-based languages so forgive me for my lack of knowledge in advance. I have some code already it's just putting the finishing touches that I'm struggling with.
Currently, the user specifies a directory in a given cell and name of the parent file in another cell. Upon clicking a button, the macro creates the parent folder using the directory and name from the parent file cell. It then creates sub folders using the values of any cells the respondent has selected upon running the macro.
I am currently struggling with the next stage of the project which is creating sub-folders (I'll just call them Grandchildren) within the subfolders. This would be easy if all of the subfolders had the same Grandchildren however, this is not the case. What I would like to do is grab the 3 values to the right of each cell which defines the name of the subfolder and use them to create the Grandchildren however I'm currently getting the 'Invalid Qualifier' message with the code I am currently using (see below).
BasePath = Range("folder_path")
'Check if the project folder already exists and if so raise and error and exit
If Dir(BasePath, vbDirectory) <> "" Then
MsgBox BasePath & " already exists", , "Error"
Else
'Create the project folder
MkDir BasePath
MsgBox "Parent folder creation complete"
'Loop through the 1st tier subfolders and create them
For Each c In ActiveWindow.RangeSelection.Cells
'create new folder path
NewFolder = BasePath & "\" & c.Value
'create folder
If fs.folderexists(NewFolder) Then
'do nothing
Else
MkDir NewFolder
End If
Next c
'Create GrandChildren
For Each d In ActiveWindow.RangeSelection.Cells
'Offset the selection to the right
For Each e In d.Offset(0, 1).Resize(1, 3).Cells
Test = e.Value
GrandChild = BasePath & "\" & d.Value & "\" & Test
If fs.folderexists(GrandChild) Then
'do nothing
Else
MkDir GrandChild
End If
Next e
Next d
MsgBox "Sub-folder creation complete"
End If
End Sub
If you require any further information please let me know.
Cheers,
Jason
I think your problem is here
Test = d.Offset(0, 1).Select
Test is a String and you are selecting a cell. You should try this:
Test = d.Offset(0,1).Value
You may find this useful, it's a simple routine I use to make ALL the folders in an entire path fed into the function.
EXAMPLE:
C:\2011\Test\
C:\2012\Test
C:\2013\Test\DeepTest\
C:\2014\Test\DeeperTest\DeeperStill
Based on the list above, this macro will attempt to create 11 directories, ones that exist already...no problem.
Option Explicit
Sub MakeDirectories()
'Author: Jerry Beaucaire, 7/11/2010
'Summary: Create directories and subdirectories based
' on the text strings listed in column A
' Parses parent directories too, no need to list separately
' 10/19/2010 - International compliant
Dim Paths As Range
Dim Path As Range
Dim MyArr As Variant
Dim pNum As Long
Dim pBuf As String
Dim Delim As String
Set Paths = Range("A:A").SpecialCells(xlConstants)
Delim = Application.PathSeparator
On Error Resume Next
For Each Path In Paths
MyArr = Split(Path, Delim)
pBuf = MyArr(LBound(MyArr)) & Delim
For pNum = LBound(MyArr) + 1 To UBound(MyArr)
pBuf = pBuf & MyArr(pNum) & Delim
MkDir pBuf
Next pNum
pBuf = ""
Next Path
Set Paths = Nothing
End Sub
There is a UDF version too and a sample file for testing found here. FYI.

Removing internal link to Word-templates via VBA

I'm trying to create a small VB-application that removes the internal link in Word Documents, to their templates.
I have found this guide
http://word.tips.net/Pages/T001437_Batch_Template_Changes.html
and am trying to modify it, to use with VBA instead of Macro programming inside of Office.
However, I'm getting stuck on how to get the Document.Open to work. Any help is appreciated.
This is supposed to run as a free-standing application, and not runt from within Word.
I'm looking for a way to perform what the Macro does, but not from within Word.
There are two pieces of bad news to give here.
1) A document has to have a template. You cannot remove it, only change it to something else.
2) Changing a template does nothing anyway. See this page.
I am wonder if the problem with the Open method is that you are trying to open ".doc" extension files, not the modern ".docx" extension files. The VBA subroutine you linked to only does ".doc" files. This VBA code does both:
Function StringEndsWith( _
ByVal strValue As String, _
CheckFor As String) As Boolean
Dim sCompare As String
Dim lLen As Long
lLen = Len(CheckFor)
If lLen > Len(strValue) Then Exit Function
sCompare = Right(strValue, lLen)
StringEndsWith = StrComp(sCompare, CheckFor, vbTextCompare) = 0
End Function
Sub ChangeTemplates()
Dim strDocPath As String
Dim strTemplateB As String
Dim strCurDoc As String
Dim docCurDoc As Document
' set document folder path and template strings
strDocPath = "C:\tmp\"
' get first doc - only time need to provide file spec
strCurDoc = Dir(strDocPath & "*.doc*")
' ready to loop (for as long as file found)
Do While strCurDoc <> ""
If (StringEndsWith(strCurDoc, ".doc") Or StringEndsWith(strCurDoc, ".docx")) Then
' open file
Set docCurDoc = Documents.Open(FileName:=strDocPath & strCurDoc)
' change the template back to Normal
docCurDoc.AttachedTemplate = ""
' save and close
docCurDoc.Close wdSaveChanges
End If
' get next file name
strCurDoc = Dir
Loop
MsgBox "Finished"
End Sub
long time between answers but may be useful to others. If you have access to the VBE of the Word document [Alt F11], and you want to remove the reference then go to "Tools/References" [top menu] and deselect it from the list of reference files. I had a similar issue where template no longer existed, but it was still being 'referenced' in the Project window, so I did the above.