🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

VBA FileSystemObject in Excel — CreateObject vs Reference, Subfolders, and Why It Does Not Open Workbooks

|

VBA FileSystemObject in Excel — CreateObject vs Reference, Subfolders, and Why It Does Not Open Workbooks

TL;DR — The FileSystemObject (FSO) is an object model for the disk: create it once, then loop Files and SubFolders and read properties like Size and DateLastModified. 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) vs Dim … As New (early binding)
  • How to recurse into subfolders, the thing Dir simply cannot do
  • Reading metadata — Size, DateLastModified, Name, ParentFolder
  • FileExists and FolderExists — 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 fso gives you GetFolder(path) and GetFile(path)
  • a Folder has a .Files collection, a .SubFolders collection, and a .Name
  • a File has .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 — Dir cannot recurse.
  • Need size, date, or type? FSO — Dir returns only a name.
  • Copying, moving, deleting, or writing text files? FSO — it is a full toolkit.
  • A quick one-folder *.xlsx loop with nothing added? Dir is 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