TL;DR — The
FileSystemObject(FSO) is an object model for the disk: create it once, then loopFilesandSubFoldersand read properties likeSizeandDateLastModified. Create it with late binding so it runs on every machine — no reference to tick:
Sub ListWithSizes()
Dim fso As Object, folder As Object, file As Object
Set fso = CreateObject("Scripting.FileSystemObject") ' late binding — no reference needed
Set folder = fso.GetFolder("C:\Reports")
For Each file In folder.Files ' a real collection, not a cursor
Debug.Print file.Name, file.Size, file.DateLastModified
Next file
End Sub
Where Dir is a terse built-in with one hidden cursor, the FileSystemObject treats the
file system the way the rest of VBA treats a workbook — as objects with properties you can loop and
inspect. That upgrade is what you buy: subfolders, file metadata, and safe nested loops. The price is one
object to create, and one decision — how you bind it — that quietly determines whether your macro runs on
anyone else's computer.
What you'll learn
- The mental model — the file system as Folders and Files, each an object with properties
- The one decision that trips everyone —
CreateObject(late binding) vsDim … As New(early binding) - How to recurse into subfolders, the thing
Dirsimply cannot do - Reading metadata —
Size,DateLastModified,Name,ParentFolder FileExistsandFolderExists— the clean way to check before you act- Why FSO opens text files but never opens an Excel workbook
The mental model: the disk as an object model
Everywhere else in VBA you work with objects — a Workbook has Sheets, a Worksheet has a Range. The
FileSystemObject extends that same idea to the disk:
- an
fsogives youGetFolder(path)andGetFile(path) - a
Folderhas a.Filescollection, a.SubFolderscollection, and a.Name - a
Filehas.Name,.Size,.DateLastModified,.ParentFolder,.Path
So instead of seeding a cursor and stepping it, you write the loop you already know — For Each file In folder.Files — and reach into each object for whatever you need. There is no hidden state, so you can nest
loops freely (files inside subfolders inside subfolders) without anything resetting underneath you.
The one decision that trips everyone: CreateObject vs New
There are two ways to make an fso, and the difference decides whether your macro survives being emailed to
a colleague. This is the number-one FileSystemObject bug.
Early binding — clean-looking, but fragile:
Dim fso As New FileSystemObject ' needs Tools > References > Microsoft Scripting Runtime
This compiles only if that machine has the Microsoft Scripting Runtime reference ticked. Write it on
your PC where the box is checked, send the file to someone whose box is not, and it fails to compile with
User-defined type not defined — before a single line runs. You get IntelliSense in exchange, but you have
tied the workbook to one machine's settings.
Late binding — the portable default:
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject") ' resolves at run time, no reference
CreateObject looks the component up by name at run time, so no reference is required and the workbook
runs anywhere Windows Scripting is installed (i.e. everywhere). You lose compile-time IntelliSense; you gain
a macro that just works when distributed. For anything you share, use late binding and declare your
variables As Object. That single choice removes the whole class of "works on my machine" reports.
Subfolders: the thing Dir cannot do
The clearest reason to leave Dir behind is a folder tree. FSO's SubFolders collection plus a recursive
Sub walks any depth, which Dir's single cursor makes impossible:
Sub WalkTree(folderPath As String)
Dim fso As Object: Set fso = CreateObject("Scripting.FileSystemObject")
ProcessFolder fso.GetFolder(folderPath), fso
End Sub
Sub ProcessFolder(fld As Object, fso As Object)
Dim file As Object, sub_ As Object
For Each file In fld.Files
If LCase(fso.GetExtensionName(file.Name)) = "xlsx" Then Debug.Print file.Path
Next file
For Each sub_ In fld.SubFolders ' recurse — Dir can't nest like this
ProcessFolder sub_, fso
Next sub_
End Sub
Each Folder carries its own .Files and .SubFolders, so nesting and recursion are safe — there is no
shared cursor to corrupt. Note fso.GetExtensionName and its siblings (GetBaseName, BuildPath,
GetParentFolderName) — string-free path helpers that replace fiddly InStrRev slicing.
Reading metadata: size, date, name
The other reason to pick FSO is that a File object knows things about itself that Dir never exposes:
For Each file In folder.Files
If file.DateLastModified < Now - 30 Then ' older than 30 days
Debug.Print file.Name & " — " & Format(file.Size / 1024, "0") & " KB"
End If
Next file
Size, DateLastModified, DateCreated, Type, Attributes — all one property away. Filtering "every
workbook changed this week" or "delete temp files over 10 MB" is trivial with FSO and painful with Dir.
FileExists and FolderExists: check before you act
FSO gives you two stateless predicates that are the cleanest way to
test for a file — and, crucially, they have no hidden cursor, so
unlike Dir you can call them anywhere, even inside a Dir loop:
If fso.FileExists("C:\Reports\March.xlsx") Then ...
If fso.FolderExists("C:\Reports\2026") Then ...
FileExists and FolderExists are distinct — Dir blurs the two — so you say exactly what you mean and
never accidentally match a folder when you wanted a file.
The trap: FSO does not open workbooks
Here is the confusion that sends people in circles. The FileSystemObject can create, read, and write
plain text files (CreateTextFile, OpenTextFile) — logs, CSVs treated as raw text, .ini files. It can
CopyFile, MoveFile, DeleteFile. What it cannot do is open an Excel workbook:
Set wb = fso.OpenTextFile("C:\Reports\March.xlsx") ' WRONG — gives you raw XML/zip bytes, not a workbook
Set wb = Workbooks.Open("C:\Reports\March.xlsx") ' RIGHT — a workbook is opened by Excel, not FSO
FSO finds and manages files; Workbooks.Open is what turns a file into a live
workbook you can read cells from. The idiomatic batch macro uses both: FSO (or Dir) to enumerate the
folder, then Workbooks.Open to open each result. Keep the two jobs separate in your head and the whole
pattern clicks.
The honest verdict: when FSO beats Dir
Dir wins on one axis only — zero setup for a flat, single-folder loop. The FileSystemObject wins
everywhere else, and the decision is mechanical:
- Need subfolders? FSO —
Dircannot recurse. - Need size, date, or type? FSO —
Dirreturns only a name. - Copying, moving, deleting, or writing text files? FSO — it is a full toolkit.
- A quick one-folder
*.xlsxloop with nothing added?Diris fine and shorter. - However you enumerate, open workbooks with
Workbooks.Open— never with FSO.
And whichever you distribute, create the object with CreateObject, not New, so it runs on every
machine and not just yours.
How ExcelMaster helps
The FileSystemObject is powerful precisely because it is an object model — but that means remembering to
bind it late for portability, to recurse SubFolders for a tree, to read the right metadata property, and
to hand each file to Workbooks.Open rather than expecting FSO to open it. Small choices, each of which
quietly decides whether the macro runs on the next person's PC.
ExcelMaster makes those choices
for you. Describe the task — "go through every subfolder under Reports, find workbooks changed this month,
and copy their totals into a summary" — and it writes a late-bound FSO walk with the recursive SubFolders
loop, the DateLastModified filter, and a matching Workbooks.Open /
Close for each hit. You describe the outcome; it wires the object model
together correctly the first time.
Frequently asked questions
What is the FileSystemObject in VBA?
The FileSystemObject (FSO) is a Windows Scripting component that exposes the file system as an object
model — Folder and File objects with collections (Files, SubFolders) and properties (Name, Size,
DateLastModified). You create it with CreateObject("Scripting.FileSystemObject") and use it to enumerate,
copy, move, delete, and read text files. Unlike Dir, it has no hidden cursor, so loops
nest safely.
CreateObject or Dim As New FileSystemObject — which should I use?
Use Set fso = CreateObject("Scripting.FileSystemObject") (late binding). It needs no reference and runs on
any machine. Dim fso As New FileSystemObject (early binding) requires the Microsoft Scripting Runtime
reference to be ticked under Tools ▸ References, so a workbook that compiles on your PC fails with
User-defined type not defined on a colleague's. Late binding is the portable default for anything you
distribute.
How do I loop through subfolders with the FileSystemObject?
Get the folder with fso.GetFolder(path), loop its .SubFolders collection, and call the same routine
recursively on each subfolder. Because every Folder has its own .Files and .SubFolders, recursion is
safe — there is no shared state to reset. This is exactly what Dir cannot do.
Can the FileSystemObject open an Excel workbook?
No. FSO opens and writes text files (OpenTextFile, CreateTextFile) and can copy, move, or delete any
file, but it cannot open a workbook. Use it to find files, then open each one with
Workbooks.Open. fso.OpenTextFile on an .xlsx returns raw bytes, not a
workbook.
How do I get a file's size or modified date in VBA?
Use the FileSystemObject: fso.GetFile(path).Size returns bytes and .DateLastModified returns the
timestamp. Inside a folder loop, read file.Size and file.DateLastModified directly on each File
object. Dir cannot give you either — it returns only the name — which is one of the main reasons to prefer
FSO for anything beyond a flat listing.
Tested in
Tested in: Excel 365 (Windows 11), VBA 7.1 — last verified 2026-08-21.
Related guides: VBA Dir · VBA Check If File Exists · VBA Open Workbook · VBA Save Workbook · VBA On Error
