【実務・中級編】【初心者向け】特定のフォルダに格納されたファイルをファイル名順にループ処理で一括添付する自動化ツール – Outlook VBA解析バイブル

スポンサーリンク

【Outlook VBA実務】ファイル名順に確実に添付!FSOを駆使した堅牢な一括添付ツールの設計と実装

開発現場でよくある要望の一つが、「指定フォルダ内にあるファイルをすべて自動でメールに添付して送信したい」というものだ。日報の集約、定型レポートの配布、請求書の送付など、手作業で行えばヒューマンエラーの温床となるこのルーティンワークは、VBAで完全に自動化すべき領域である。

しかし、ネット上に散見される「とりあえず動くコード」をそのままプロダクション環境に投入すると、思わぬ罠に足を取られる。
「ファイルの並び順がバラバラで宛先を混乱させた」「存在しないフォルダを指定してフリーズした」「ファイル名に日本語が含まれていて文字化けした」――これらはプロのエンジニアとして絶対に避けなければならない。

今回は、FileSystemObject(FSO)の真価を引き出し、ファイル名順のソート制御と頑健な例外処理を兼ね備えた、実務直結型のメール自動作成ツールを構築する。

—

1. なぜ「Dir関数」ではなく「FileSystemObject」を使うべきなのか?

VBAでファイル走査を行う際、古くから存在する `Dir` 関数を使うコードをよく見かける。しかし、実務において `Dir` 関数の使用は推奨しない。

  • `Dir` 関数の限界:
  • 実行環境(OSのファイルシステム)の内部的なキャッシュや状態に依存するため、ファイル名順に綺麗に取得できる保証がない。
  • 再帰的なフォルダ走査や、ファイル属性(作成日時、サイズ等)の詳細な制御が苦手である。
  • 複数プロシージャからネストして呼び出すと、内部ポインタが干渉してバグの温床になる。

一方、`Scripting.FileSystemObject`(以下、FSO)は、オブジェクト指向的にファイルシステムを操作できる強力なツールだ。コレクションとして取得したファイルを適切に配列やDictionaryに格納してソートすることで、「常に意図したファイル名順」でメールに添付することが可能になる。

—

2. 実務仕様のプロダクションコード

以下のコードは、指定したフォルダ内の全ファイルをファイル名昇順で取得し、新規メール(`MailItem`)の添付ファイルとして一括アタッチする完全版のプロシージャだ。

開発プロジェクトの標準モジュールにそのまま貼り付けて使用してほしい。

Option Explicit

‘ =========================================================================
‘ 処理名 : CreateEmailWithSortedAttachments
‘ 概要 : 指定フォルダ内のファイルをファイル名順に走査し、Outlookメールに一括添付する
‘ 備考 : Microsoft Scripting Runtime への参照設定を推奨(Lヘイト回避のため早期バインディング)
‘ =========================================================================
Public Sub CreateEmailWithSortedAttachments()
‘ ———————————————————————
‘ 設定エリア(環境に合わせて書き換えてください)
‘ ———————————————————————
Const TARGET_FOLDER As String = “C:\Reports\Daily\” ‘ 添付元フォルダ(末尾の\に注意)
Const MAIL_TO As String = “sample@example.com”
Const MAIL_SUBJECT As String = “【自動送信】本日の日報データ一式”

‘ ———————————————————————
‘ 変数宣言
‘ ———————————————————————
Dim fso As Object
Dim targetFolderObj As Object
Dim fileObj As Object
Dim fileCollection As Object

‘ ソート用の動的配列
Dim filePaths() As String
Dim fileCount As Long

Dim outlookApp As Object
Dim mailItemObj As Object

Dim i As Long
Dim tempPath As String

‘ ———————————————————————
習得フェーズ1: FileSystemObjectの初期化と存在確認
‘ ———————————————————————
Set fso = CreateObject(“Scripting.FileSystemObject”)

If Not fso.FolderExists(TARGET_FOLDER) Then
MsgBox “指定されたフォルダが存在しません。” & vbCrLf & TARGET_FOLDER, vbCritical, “エラー”
Exit Sub
End If

Set targetFolderObj = fso.GetFolder(TARGET_FOLDER)
Set fileCollection = targetFolderObj.Files

If fileCollection.Count = 0 Then
MsgBox “指定されたフォルダ内にファイルが存在しません。”, vbExclamation, “通知”
Exit Sub
End If

‘ ———————————————————————
習得フェーズ2: ファイルパスを配列に格納し、ファイル名順(昇順)にソート
‘ ———————————————————————
fileCount = fileCollection.Count
ReDim filePaths(1 To fileCount)

i = 1
For Each fileObj In fileCollection
filePaths(i) = fileObj.Path
i = i + 1
Next fileObj

‘ バブルソートによるファイル名順の担保(ファイル数が数千件でなければ実用的な速度)
Call SortFilePaths(filePaths)

‘ ———————————————————————
習得フェーズ3: Outlookオブジェクトの生成とメール構築
‘ ———————————————————————
On Error GoTo ErrorHandler

‘ 既存のOutlookインスタンスを取得、なければ新規作成
On Error Resume Next
Set outlookApp = GetObject(, “Outlook.Application”)
If outlookApp Is Nothing Then
Set outlookApp = CreateObject(“Outlook.Application”)
End If
On Error GoTo ErrorHandler ‘ エラーハンドラを元に戻す

Set mailItemObj = outlookApp.CreateItem(0) ‘ olMailItem = 0

With mailItemObj
.To = MAIL_TO
.Subject = MAIL_SUBJECT
.Body = “関係者各位” & vbCrLf & vbCrLf & _
“お疲れ様です。” & vbCrLf & _
“指定フォルダ内の添付ファイル一式を送付いたします。” & vbCrLf & _
“ご確認のほど、よろしくお願い申し上げます。”

‘ ソート済み配列から順にファイルを添付
For i = 1 To UBound(filePaths)
.Attachments.Add filePaths(i)
Next i

‘ 【重要】プロダクション設計の鉄則
‘ 自動送信(.Send)は誤爆のリスクがあるため、プレビュー(.Display)を推奨
.Display
End With

‘ ———————————————————————
‘ 終了処理
‘ ———————————————————————
MsgBox “ファイルの添付が完了しました。内容を確認して送信してください。”, vbInformation, “完了”
GoTo SafeExit

ErrorHandler:
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “システムエラー”

SafeExit:
‘ オブジェクトの解放(メモリリーク防止)
Set mailItemObj = Nothing
Set outlookApp = Nothing
Set fileCollection = Nothing
Set targetFolderObj = Nothing
Set fso = Nothing
End Sub

‘ =========================================================================
‘ 補助プロシージャ : 配列内のファイルパスをファイル名ベースで昇順ソート
‘ =========================================================================
Private Sub SortFilePaths(ByRef paths() As String)
Dim i As Long, j As Long
Dim temp As String
Dim nameA As String, nameB As String
Dim fsoTemp As Object

Set fsoTemp = CreateObject(“Scripting.FileSystemObject”)

For i = LBound(paths) To UBound(paths) – 1
For j = i + 1 To UBound(paths)
‘ フルパスからファイル名のみを抽出して比較
nameA = fsoTemp.GetFileName(paths(i))
nameB = fsoTemp.GetFileName(paths(j))

If StrComp(nameA, nameB, vbTextCompare) > 0 Then
temp = paths(i)
paths(i) = paths(j)
paths(j) = temp
End If
Next j
Next i

Set fsoTemp = CreateObject(“Scripting.FileSystemObject”) ‘ 解放
Set fsoTemp = Nothing
End Sub

—

3. コードのアーキテクチャと設計の急所

このコードが「単なる動くコード」ではなく「プロダクション品質」たる所以を、エンジニアの視点から解説する。

① 配列への退避と自前ソートの必然性

FSOの `Folder.Files` コレクションは、OSが返す順序(通常はファイルシステムの内部インデックス順)で格納される。そのため、`For Each` で回すだけでは「報告書_03.xlsx」「報告書_01.xlsx」「報告書_02.xlsx」といったバラバラの順番で添付されてしまう。
上記コードでは、一度すべてのパスを配列に回収し、補助プロシージャ `SortFilePaths` でファイル名(`GetFileName`)を基準にバブルソートをかけている。これにより、人間がエクスプローラーで見るときと完全に一致した順序でメールに添付される。

② メモリリークとCOMオブジェクトの適切な解放

VBA(特にOfficeオートメーション)において、`CreateObject` で生成したCOMオブジェクト(OutlookやFSO)の参照を放置すると、見えないところでプロセスが残存し(ゾンビプロセス)、PCの動作不良やファイルロックの原因になる。
プロシージャの最後(`SafeExit` ラベル)で、使用したオブジェクト変数に `Nothing` を代入して確実にメモリを解放する設計を徹底している。

③ `.Send` ではなく `.Display` を採用する理由

業務自動化ツールにおいて、コードが意図通りに動くか確認する前の `.Send`(即時送信)は「時限爆弾」に等しい。宛先の誤設定やファイルの取り違えがあった場合に取り返しがつかない。
そのため、本ツールであえて `.Display`(画面表示)を採用している。オペレーターが添付ファイルの一覧や宛先を目視で最終確認し、自分の手で「送信」ボタンを押す。この「ヒトとシステムの協調」こそが、実務で最も安全な自動化の形である。

—

4. 現場へ導入する際のアドバイス

  • ネットワークドライブ上のフォルダを指定する場合:

社内共有サーバー(UNCパス:`\\server\share\folder\`)を指定する場合、ネットワークの応答速度によってFSOの `FolderExists` がタイムアウトを起こすことがある。その場合は、事前にエクスプローラー等でパスにアクセス可能か確認するロジック、あるいはエラーハンドリングの強化が必要になる。

  • ファイル名の文字コード:

Windows環境であれば日本語ファイル名も問題なく処理できるが、特殊文字(半角カタカナや環境依存文字)が含まれている場合、API側で例外が発生することがある。運用ルールとして「ファイル名に使用する文字は半角英数・アンダーバー・ハイフンに制限する」などの規約を設けると、システムの堅牢性がさらに高まる。

プログラミングの本質は「コードを書くこと」ではなく「例外を予測し、破綻しない仕組みを作ること」にある。このテンプレートをベースに、あなたの現場の業務をスマートに、そして強固に自動化してほしい。

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