【VBA】Dir 関数の罠を撃退!エラーとファイルを完全判別する「安全なフォルダ存在確認関数」(テストコード付き)
前回の「ファイル存在確認」に続き、今回は「指定したフォルダ(ディレクトリー)が存在するかどうか」を判定する処理を解説します。
「フォルダがあるか確認するだけなら Dir(path, vbDirectory) で一発では?」と思われがちですが、実はVBA標準の Dir 関数でフォルダ確認を行うと、ファイル存在確認以上に凶悪な誤判定やエラーに直面します。
今回は、フォルダ存在確認で陥りがちな罠を整理し、「契約による設計(DbC)」のコメントフォーマットに則った安全な汎用関数を作成します。
1. フォルダ存在確認で陥りやすい「3つの罠」
- 罠①:
Dir(path, vbDirectory)は「ファイル」もヒットしてしまう
Dir("C:\Test\sample.txt", vbDirectory)のように、ファイルパスを渡してもsample.txtという文字列が返ってきます。つまりDirの第2引数にvbDirectoryを指定しても「フォルダのみを検索する」という意味にはならず、ファイルも判定対象に含まれてしまうため誤判定の原因になります。 - 罠②: パス末尾の
\(スラッシュ・バックスラッシュ)による挙動の違い
GetAttrやDirを使う際、C:\TestFolder\のように末尾に\が付いていると実行時エラー(エラー53: ファイルが見つかりません)になるというVBA独自の癖があります。ただし、C:\やD:\といったドライブルートだけは末尾の\が必須であるため、一括で削除すると崩れてしまいます。 - 罠③: 権限不足やネットワーク切断によるクラッシュ
ファイル時と同様、アクセス権限がない共有フォルダや、切断されたネットワークドライブ(\\Server\Share)を指定すると、DirやGetAttrが実行時エラー 70 や 76 でマクロを強制終了させます。
2. 実装:安全なフォルダ存在確認関数 (FolderExists)
これらの罠をすべてクリアし、ファイルとの誤認識を防ぎながら安全に Boolean(True / False)を返す関数です。
' ==============================================================================
' [機能名] 安全なフォルダ存在確認
'
' [処理内容]
' ・指定されたパスに「フォルダ」が存在するかどうかを確認します。
' ・ファイルパスが渡された場合や、アクセス権限不足・パス不正の場合は False を返します。
'
' [引数]
' @param {String} folderPath : 必須 : 確認対象のフォルダパス
'
' [事前条件 (Requires)]
' ・特になし(空文字やファイルパス、末尾 \ あり/なし いずれも受け付けます)。
'
' [事後条件 (Ensures)]
' ・指定パスにアクセス可能で、かつ「フォルダ」が存在する場合 : True を返す。
' ・フォルダが存在しない、ファイルである、または権限不足でアクセス不可の場合 : False を返す。
'
' [戻り値]
' @return {Boolean} フォルダの存在判定結果
'
' [例外処理 (Throws)]
' ・アクセス権限エラー(70)やパス不在(53/76)等は内部で捕捉し、[WARNING] ログを出力して False を返す。
'
' [履歴]
' 2026/10/08 [作成者名] 新規作成
' ==============================================================================
Public Function FolderExists(ByVal folderPath As String) As Boolean
' 前後の空白を除去
Dim cleanPath As String
cleanPath = Trim(folderPath)
' 1. 空文字チェック
If cleanPath = "" Then
FolderExists = False
Exit Function
End If
' 2. パス区切り文字を '\' に統一 (/ を \ に変換)
cleanPath = Replace(cleanPath, "/", "\")
' 3. 末尾の '\' の正規化
' ドライブルート (例: "C:\" や "D:\") 以外で、末尾に '\' がある場合は除去する
' (※ GetAttr は通常フォルダの末尾に '\' がついているとエラーになるため)
If Right$(cleanPath, 1) = "\" And Not cleanPath Like "?:\" Then
cleanPath = Left$(cleanPath, Len(cleanPath) - 1)
End If
' 4. GetAttr による属性確認(エラーを内部でキャッチ)
On Error Resume Next
Dim fileAttr As VbFileAttribute
fileAttr = GetAttr(cleanPath)
Dim errNum As Long: errNum = Err.Number
Dim errDesc As String: errDesc = Err.Description
On Error GoTo 0
' エラーが発生した場合(パス未存在、アクセス権限不足など)
If errNum <> 0 Then
' 存在しないパスの場合は静かに False(権限エラー等の場合はログを残す)
If errNum <> 53 And errNum <> 76 Then
Debug.Print "[WARNING] フォルダアクセス不可 (Err:" & errNum & " - " & errDesc & ") Path: " & cleanPath
End If
FolderExists = False
Exit Function
End If
' 5. ビット演算による「フォルダ属性」の確定
' 対象がファイルではなく、本当にフォルダ (vbDirectory) か判定
If (fileAttr And vbDirectory) = vbDirectory Then
FolderExists = True
Else
' 存在するが「ファイル」だった場合は False
FolderExists = False
End If
End Function
' [機能名] 安全なフォルダ存在確認
'
' [処理内容]
' ・指定されたパスに「フォルダ」が存在するかどうかを確認します。
' ・ファイルパスが渡された場合や、アクセス権限不足・パス不正の場合は False を返します。
'
' [引数]
' @param {String} folderPath : 必須 : 確認対象のフォルダパス
'
' [事前条件 (Requires)]
' ・特になし(空文字やファイルパス、末尾 \ あり/なし いずれも受け付けます)。
'
' [事後条件 (Ensures)]
' ・指定パスにアクセス可能で、かつ「フォルダ」が存在する場合 : True を返す。
' ・フォルダが存在しない、ファイルである、または権限不足でアクセス不可の場合 : False を返す。
'
' [戻り値]
' @return {Boolean} フォルダの存在判定結果
'
' [例外処理 (Throws)]
' ・アクセス権限エラー(70)やパス不在(53/76)等は内部で捕捉し、[WARNING] ログを出力して False を返す。
'
' [履歴]
' 2026/10/08 [作成者名] 新規作成
' ==============================================================================
Public Function FolderExists(ByVal folderPath As String) As Boolean
' 前後の空白を除去
Dim cleanPath As String
cleanPath = Trim(folderPath)
' 1. 空文字チェック
If cleanPath = "" Then
FolderExists = False
Exit Function
End If
' 2. パス区切り文字を '\' に統一 (/ を \ に変換)
cleanPath = Replace(cleanPath, "/", "\")
' 3. 末尾の '\' の正規化
' ドライブルート (例: "C:\" や "D:\") 以外で、末尾に '\' がある場合は除去する
' (※ GetAttr は通常フォルダの末尾に '\' がついているとエラーになるため)
If Right$(cleanPath, 1) = "\" And Not cleanPath Like "?:\" Then
cleanPath = Left$(cleanPath, Len(cleanPath) - 1)
End If
' 4. GetAttr による属性確認(エラーを内部でキャッチ)
On Error Resume Next
Dim fileAttr As VbFileAttribute
fileAttr = GetAttr(cleanPath)
Dim errNum As Long: errNum = Err.Number
Dim errDesc As String: errDesc = Err.Description
On Error GoTo 0
' エラーが発生した場合(パス未存在、アクセス権限不足など)
If errNum <> 0 Then
' 存在しないパスの場合は静かに False(権限エラー等の場合はログを残す)
If errNum <> 53 And errNum <> 76 Then
Debug.Print "[WARNING] フォルダアクセス不可 (Err:" & errNum & " - " & errDesc & ") Path: " & cleanPath
End If
FolderExists = False
Exit Function
End If
' 5. ビット演算による「フォルダ属性」の確定
' 対象がファイルではなく、本当にフォルダ (vbDirectory) か判定
If (fileAttr And vbDirectory) = vbDirectory Then
FolderExists = True
Else
' 存在するが「ファイル」だった場合は False
FolderExists = False
End If
End Function
3. 単体テスト用コード
正常なフォルダ、ドライブルート、末尾 \ 付きパス、存在しないフォルダ、そして「あえてファイルパスを渡すケース」などを網羅したテストプロシージャです。
エントリーポイント用のアスペクト(CAspect)を併用し、テスト実行ログを自動記録します。
' ==============================================================================
' [機能名] FolderExists 関数の網羅的テスト
' ==============================================================================
Public Sub Test_FolderExists()
' エントリーポイント用アスペクト(実行時間・ログ自動化)
Dim aspect As New CAspect: aspect.Begin "Test_FolderExists"
Debug.Print "=================================================="
Debug.Print " FolderExists 単体テスト開始"
Debug.Print "=================================================="
' 1. 正常系:自ブックが存在するフォルダ(末尾 \ なし)
Dim validFolderPath As String
validFolderPath = ThisWorkbook.Path
If FolderExists(validFolderPath) Then
Debug.Print "[PASS] 正常系: 既存フォルダ判定 OK"
Else
Debug.Print "[FAIL] 正常系: 既存フォルダ判定 NG"
End If
' 2. 正常系:末尾に '\' が付いたフォルダパス
If FolderExists(validFolderPath & "\") Then
Debug.Print "[PASS] 表記ゆれ系: 末尾\付きフォルダ判定 OK"
Else
Debug.Print "[FAIL] 表記ゆれ系: 末尾\付きフォルダ判定 NG"
End If
' 3. 正常系:ドライブルート (C:\)
If FolderExists("C:\") Then
Debug.Print "[PASS] ドライブルート: C:\ 判定 OK"
Else
Debug.Print "[FAIL] ドライブルート: C:\ 判定 NG"
End If
' 4. 異常系:存在しないフォルダ
If Not FolderExists("C:\NonExistentFolder_9999") Then
Debug.Print "[PASS] 異常系: 存在しないフォルダ判定 OK"
Else
Debug.Print "[FAIL] 異常系: 存在しないフォルダ判定 NG"
End If
' 5. 誤認識防止系:フォルダではなく「ファイルパス」を渡した場合
Dim currentFilePath As String
currentFilePath = ThisWorkbook.FullName
If Not FolderExists(currentFilePath) Then
Debug.Print "[PASS] ファイル排除系: ファイルパスの誤認排除 OK"
Else
Debug.Print "[FAIL] ファイル排除系: ファイルをフォルダと誤認識 NG"
End If
Debug.Print "=================================================="
End Sub
' [機能名] FolderExists 関数の網羅的テスト
' ==============================================================================
Public Sub Test_FolderExists()
' エントリーポイント用アスペクト(実行時間・ログ自動化)
Dim aspect As New CAspect: aspect.Begin "Test_FolderExists"
Debug.Print "=================================================="
Debug.Print " FolderExists 単体テスト開始"
Debug.Print "=================================================="
' 1. 正常系:自ブックが存在するフォルダ(末尾 \ なし)
Dim validFolderPath As String
validFolderPath = ThisWorkbook.Path
If FolderExists(validFolderPath) Then
Debug.Print "[PASS] 正常系: 既存フォルダ判定 OK"
Else
Debug.Print "[FAIL] 正常系: 既存フォルダ判定 NG"
End If
' 2. 正常系:末尾に '\' が付いたフォルダパス
If FolderExists(validFolderPath & "\") Then
Debug.Print "[PASS] 表記ゆれ系: 末尾\付きフォルダ判定 OK"
Else
Debug.Print "[FAIL] 表記ゆれ系: 末尾\付きフォルダ判定 NG"
End If
' 3. 正常系:ドライブルート (C:\)
If FolderExists("C:\") Then
Debug.Print "[PASS] ドライブルート: C:\ 判定 OK"
Else
Debug.Print "[FAIL] ドライブルート: C:\ 判定 NG"
End If
' 4. 異常系:存在しないフォルダ
If Not FolderExists("C:\NonExistentFolder_9999") Then
Debug.Print "[PASS] 異常系: 存在しないフォルダ判定 OK"
Else
Debug.Print "[FAIL] 異常系: 存在しないフォルダ判定 NG"
End If
' 5. 誤認識防止系:フォルダではなく「ファイルパス」を渡した場合
Dim currentFilePath As String
currentFilePath = ThisWorkbook.FullName
If Not FolderExists(currentFilePath) Then
Debug.Print "[PASS] ファイル排除系: ファイルパスの誤認排除 OK"
Else
Debug.Print "[FAIL] ファイル排除系: ファイルをフォルダと誤認識 NG"
End If
Debug.Print "=================================================="
End Sub
4. 実行結果(イミディエイトウィンドウ)
10:00:00 [START] Test_FolderExists (User: developer)
==================================================
FolderExists 単体テスト開始
==================================================
[PASS] 正常系: 既存フォルダ判定 OK
[PASS] 表記ゆれ系: 末尾\付きフォルダ判定 OK
[PASS] ドライブルート: C:\ 判定 OK
[PASS] 異常系: 存在しないフォルダ判定 OK
[PASS] ファイル排除系: ファイルパスの誤認排除 OK
==================================================
10:00:00 [END] Test_FolderExists (0.001s)
==================================================
FolderExists 単体テスト開始
==================================================
[PASS] 正常系: 既存フォルダ判定 OK
[PASS] 表記ゆれ系: 末尾\付きフォルダ判定 OK
[PASS] ドライブルート: C:\ 判定 OK
[PASS] 異常系: 存在しないフォルダ判定 OK
[PASS] ファイル排除系: ファイルパスの誤認排除 OK
==================================================
10:00:00 [END] Test_FolderExists (0.001s)
5. まとめ
GetAttr+ ビット演算 (fileAttr And vbDirectory) が最強:Dir関数に頼らずGetAttrを使うことで、ファイルとフォルダを厳密に区別できます。- 末尾の
\トリムは「ドライブルート除外」が必須:C:\Testの末尾\は削る必要がありますが、C:\の\を削るとC:(カレントドライブ相対パス)に意味が変わってしまうため、Not cleanPath Like "?:\"で条件分岐するのがポイントです。 - エラーは関数内で隠蔽し、事後条件をシンプルに保つ:アクセス権限不足やネットワーク未接続などの予期せぬエラーをすべて内部で安全に処理(
Falseを返却)することで、呼び出し側はIf FolderExists(path) Thenと書くだけで安全に後続処理へ進むことができます。
PR