【実務・中級編】VBAの「再帰呼び出し」でフォルダ階層を全探索する:ファイル操作の自動化 – Excel VBA解析バイブル

スポンサーリンク

Excel VBAを掌握する極限の知見:再帰呼び出しとFSOによるフォルダ階層全探索の極意

開発現場を見渡すと、いまだに `Dir関数` を入れ子にして深部フォルダの探索に苦悶しているコードを見かける。
「サブフォルダも含めて全てのExcelファイルを集計したい」
「特定の古いログファイルを一網打尽に削除したい」

この極めて一般的な要件に対し、場当たり的なループ処理を組むと、コードは途端にスパゲッティ化し、無限ループやメモリリーク、想定外のアクセス権エラーの温床となる。

今回は、業務自動化エンジニアとして数千の現場を救ってきた私から、FileSystemObject(以下、FSO)と「再帰呼び出し(Recursion)」を組み合わせた、圧倒的に堅牢かつ美しいフォルダ全探索アルゴリズムを伝授する。

中途半端な知識で書かれたコードとは一線を画す、プロダクションクオリティの知見を心して読み解いてほしい。

1. なぜ「再帰呼び出し」と「FSO」なのか?

ディープなフォルダ構造に立ち向う唯一の解

業務で扱うファイルサーバーの階層構造は、人間が作る以上、何階層に及ぶか予測不能だ。これを `For` や `Do` の静的なループで解決しようとすると、階層の深さに応じてコードを無限にネストさせなければならず、実質的に不可能となる。

ここで「再帰呼び出し(自分自身を呼び出すプロシージャ)」の出番だ。
「フォルダを受け取る → 中のファイルを処理する → サブフォルダを見つけたら、自分自身(フォルダ探索プロシージャ)をもう一度呼び出す」
この極めてシンプルかつ数学的な美しさを持つアプローチにより、無限の深さを持つツリー構造をわずか数十行のコードで完全制圧できる。

Dir関数の限界とFSOの優位性

VBA標準の `Dir関数` は、メモリ上に状態を保持するシングルトン的な挙動をするため、「ループの途中で別のDir関数を呼び出す(=サブフォルダを再帰的に探索する)」と状態が破壊されるという致命的な欠陥がある。

一方、`Scripting.FileSystemObject` はオブジェクト指向的にインスタンスを独立して扱えるため、多重ループや再帰処理であっても状態が混濁することがない。ファイルシステムを操作するなら、FSO以外の選択肢は存在しないと言っていい。

2. 堅牢な設計:バグを生まないための3箇条

プロダクション環境(本番環境)に耐えうるツールを作るには、以下の3点をアーキテクチャの根底に組み込む必要がある。

1. アクセス拒否(Permission Denied)のハンドリング
企業内の共有サーバーには、管理者ですらアクセス権を持たないプロパティや、特殊なシステムフォルダが混ざっている。エラー対策(`On Error Resume Next` の局所適用)を怠ると、ツールは一瞬でクラッシュする。
2. 早期イグジットと参照の解放
FSOやFolderオブジェクトは、スコープを抜けるタイミングを意識し、メモリリークを防ぐ設計にする(特に大量のファイルを扱うバッチ処理では死活問題となる)。
3. 無限ループ・シンボリックリンクの罠
Windowsのジャンクションやショートカットが生む循環参照に対し、カウンターやパスの検証で身を守る防衛策を講じる。

3. 【コピペ即稼働】プロダクション・コード例

それでは、実務でそのまま使えるモジュールコードを公開する。
このコードは、指定したルートフォルダ以下を完全に走査し、見つかったすべてのファイルのパスをイミディエイトウィンドウに出力(必要に応じてシートへ転記)する汎用エンジンである。

Option Explicit

‘ =========================================================================
‘ 模範的なフォルダ全探索モジュール
‘ 依存関係: Microsoft Scripting Runtime (FSO)
‘ =========================================================================
Public Sub RunFolderExploration()
Dim fso As Object
Dim targetPath As String
Dim startTime As Double

startTime = Timer

‘ 1. 探索の起点となるフォルダを指定(例としてデスクトップや任意の共有フォルダ)
‘ ※環境に合わせて書き換えてください
targetPath = “C:\Work\TargetFolder”

‘ FSOの初期化(後期バインディングを使用し、参照設定の煩わしさを排除)
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ フォルダが存在するかチェック
If Not fso.FolderExists(targetPath) Then
MsgBox “指定されたフォルダが存在しません: ” & vbCrLf & targetPath, vbCritical, “パスエラー”
Exit Sub
End If

Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual

Debug.Print “=== 探索開始: ” & targetPath & ” ===”

‘ 再帰プロシージャの呼び出し
Call ExploreFolderRecursive(fso.GetFolder(targetPath))

Debug.Print “=== 探索完了 (処理時間: ” & Format(Timer – startTime, “0.00秒”) & ” ) ===”

Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True

MsgBox “全ファイルの探索・処理が完了しました。”, vbInformation, “完了”

Set fso = Nothing
End Sub

‘ ————————————————————————-
‘ 核心部:再帰的にフォルダを掘り下げるプロシージャ
‘ ————————————————————————-
Private Sub ExploreFolderRecursive(ByVal currentFolder As Object)
Dim subFolder As Object
Dim fileItem As Object

‘ アクセス権限がないフォルダ等でエラーが発生することを想定し、局所的にエラー対策
On Error GoTo ErrorHandler

‘ ———————————————————————
‘ 1. 現在の階層にあるファイルを処理
‘ ———————————————————————
For Each fileItem In currentFolder.Files
‘ — [ここに実際のファイル処理を記述] —
‘ 例: 拡張子が .xlsx のファイルのみを対象にする場合
If LCase(Right(fileItem.Name, 5)) = “.xlsx” Then
‘ デバッグ出力(本番ではシートへの書き込みや別処理に置き換える)
Debug.Print “ファイル発見: ” & fileItem.Path
End If
‘ ————————————–
Next fileItem

‘ ———————————————————————
‘ 2. サブフォルダを巡回し、自分自身を再帰呼び出し
‘ ———————————————————————
For Each subFolder In currentFolder.SubFolders
‘ 再帰呼び出し(Deep Dive)
Call ExploreFolderRecursive(subFolder)
Next subFolder

Exit_Handler:
Exit Sub

ErrorHandler:
‘ 権限エラー(エラー番号 70: 書き込み/アクセス権限なし)などをスキップして処理を継続
Debug.Print “【スキップ】アクセスできなかったフォルダ: ” & currentFolder.Path & ” (理由: ” & Err.Description & “)”
Resume Exit_Handler
End Sub

4. コードの急所:プロフェッショナルの解説

上記のコードに散りばめられた「現場の知見」を解説しよう。

① 後期バインディング(`CreateObject(“Scripting.FileSystemObject”)`)の採用

早期バインディング(`Dim fso As New FileSoup…` などの参照設定)は開発時には便利だが、別環境のPC(Excelのバージョン違いやOfficeのビット数違い)で動かした際に「コンパイルエラー:プロジェクトまたはライブラリが見つからない」という悪名高いエラーを引き起こす。
配布するツールであれば、後期バインディングでインスタンスを動的生成するのが鉄則だ。

② 局所的なエラーハンドリング (`On Error GoTo ErrorHandler`)

大規模なファイルサーバーを探索すると、必ず「SYSTEM」しか触れない隠しフォルダや、アクセス権が剥奪されたユーザープロファイルにぶつかる。
ここで全体のエラーを止めてしまうと、ツールはそこでフリーズ・停止する。フォルダ単位で `On Error` をトラップし、アクセス不可なフォルダは「ログを出して華麗にスルー(Resume Exit_Handler)」するのが、止まらないシステムを作る極意である。

③ 描画・計算の完全停止

ファイルやフォルダの数が数万個に及ぶ場合、`Debug.Print` やシートへの書き込みが挟まると、OSの描画処理がボトルネックになり激遅になる。
プロシージャの最初と最後で `ScreenUpdating` と `Calculation` を制御することは、バックエンド処理の基本中の基本だ。

5. データベース連携・実務展開へのステップアップ

この再帰探索エンジンを手に入れたことで、あなたの自動化の引き出しは劇的に広がる。ここからさらに実務で応用するためのヒントを授けよう。

  • SQL ServerやSQLiteへのバルクインサート

見つけたファイルのパスや更新日時、サイズをその場でVBAから直接DBに投げ込むことで、社内ファイル資産の「インデックス検索エンジン」が数時間で自作できる。

  • 非同期・マルチスレッド的なアプローチ(限界の突破)

VBA自体はシングルスレッドだが、フォルダのトップ階層ごとに処理を分散させたい場合は、シェル(WScript.Shell)経由で複数のExcelインスタンスを起動するオーケストレーション設計に発展させることも可能だ。

「なぜその書き方をするのか」の理由を突き詰めれば、VBAは単なるマクロの域を超え、堅牢なエンタープライズ・アプリケーションの駆動エンジンへと変貌する。
ぜひ、あなたの現場の自動化プロセスにこの再帰エンジンの知見を組み込み、圧倒的な効率化を成し遂げてほしい。

タイトルとURLをコピーしました