【実務・中級編】【初心者向け】特定のフォルダ内の全プロジェクトファイルを一括で開いて閉じるループ処理の基本 – Project VBA解析バイブル

スポンサーリンク

【Project VBA】複数.mppファイルの一括処理で「メモリリーク」「アプリフリーズ」を徹底排除する極限のループ設計

Microsoft Project(以下、MS Project)の運用において、避けて通れないのが「特定フォルダ内に存在する大量のプロジェクトファイル(.mpp)に対する一括処理」です。

ベースラインの一括設定、特定カスタムフィールドの横断抽出、進捗データの一括更新など、自動化のニーズは現場に溢れています。しかし、ネット上に転がっている一般的なExcel VBAのコードをそのままProject VBAに移植すると、処理の途中でフリーズする、メモリが逼迫する、謎のダイアログで停止するといったトラブルに必ず直面します。

本記事では、単に「ファイルを開いて閉じる」だけの退屈なサンプルではなく、オブジェクトのライフサイクルとメモリ管理を完全に制御し、業務自動化ツールとして破綻しないプロダクションコードの設計思想を徹底解説します。

1. なぜ「素朴なDirループ」は現場で崩壊するのか?

Excel VBA経験者がProject VBAで一括処理を書く際、最も陥りやすい罠が「MS Project特有の重厚なUI描画メカニズムとCOMオブジェクトの残存」です。

欠陥コードが引き起こす3大障害

1. 画面描画オーバーヘッドによる低速化
ファイルを開くたびにMS ProjectのUIがガントチャートを再描画し、数GBのメモリを無駄に消費します。
2. インタラクティブ・ダイアログによるスクリプト停止
「リソースの割り当てに関する警告」や「読み取り専用推奨」などの確認ダイアログが途中でポップアップし、夜間バッチが停止します。
3. `ActiveProject` 参照によるメモリリークと誤操作
`ActiveProject` に依存したコードを書くと、非同期でファイルが開いた際に参照がズレるか、COM参照が残ってファイルがロックされたままになります。

プロフェッショナルが構築すべきは、「画面を描画させず、ダイアログを黙らせ、オブジェクトの参照を確実に解放する」堅牢なパイプラインです。

2. 堅牢な一括処理を構築するための「鉄則3カ条」

コードを見る前に、アーキテクチャの根幹となる3つの鉄則を理解してください。

鉄則①:UIとアラートの完全遮断

処理開始時に `ScreenUpdating = False` および `DisplayAlerts = False` を徹底します。これにより、余計な再描画とポップアップをシャットアウトします。

鉄則②:`ActiveProject` の全面禁止(明示的オブジェクト参照)

開いたファイルは必ず `Project` 型の変数で保持します。操作対象を常に明示することで、マルチファイル環境下での誤操作を防ぎ、処理終了時の安全な破棄を保証します。

鉄則③:関数の分離(関心事の分離)

「フォルダ内のファイルを走査するループ処理」と「1つのファイルに対して行う実処理(ペイロード)」を明確に分離します。これにより保守性が格段に向上します。

3. コピペで動作するプロダクションコード例

以下のコードは、指定フォルダ内のすべての `.mpp` ファイルを順次安全に開き、処理を施して閉じる完全な標準モジュールコードです。

Option Explicit

‘ ==============================================================================
‘ 処理名:BatchProcessMPPFiles
‘ 概要 :指定フォルダ内の全.mppファイルを安全に巡回処理するメインルーチン
‘ 担当 :チーフアーキテクト設計
‘ ==============================================================================
Public Sub BatchProcessMPPFiles()
Dim targetFolderPath As String
Dim fileName As String
Dim fullPath As String
Dim processedCount As Long
Dim errorCount As Long

‘ 1. 対象フォルダの指定(末尾に円マーク/スラッシュを保証)
targetFolderPath = “C:\ProjectManagement\TargetFiles\”
If Right(targetFolderPath, 1) <> “\” Then
targetFolderPath = targetFolderPath & “\”
End If

‘ フォルダが存在するか事前検証
If Dir(targetFolderPath, vbDirectory) = “” Then
MsgBox “指定されたフォルダが存在しません: ” & targetFolderPath, vbCritical, “エラー”
Exit Sub
End If

‘ 2. アプリケーション状態の退避と無効化(高速化・堅牢化の核心)
On Error GoTo FatalErrorHandler
Application.ScreenUpdating = False
Application.DisplayAlerts = False

‘ 3. Dir関数によるファイル検索ループの開始
fileName = Dir(targetFolderPath & “.mpp”)

Do While fileName <> “”
fullPath = targetFolderPath & fileName

‘ 個別ファイルの処理を実行(エラーハンドリングの局所化)
If ProcessSingleProjectFile(fullPath) Then
processedCount = processedCount + 1
Else
errorCount = errorCount + 1
End If

‘ 次のファイルを検索(Dir関数の状態を破綻させないためループ内で再呼び出し)
fileName = Dir()
Loop

‘ 4. 正常終了時の処理
MsgBox “一括処理が完了しました。” & vbCrLf & _
“成功: ” & processedCount & ” 件” & vbCrLf & _
“失敗: ” & errorCount & ” 件”, vbInformation, “処理結果”

CleanUp:
‘ 後始末:アプリケーション設定を必ず元に戻す
Application.DisplayAlerts = True
Application.ScreenUpdating = True
Exit Sub

FatalErrorHandler:
MsgBox “予期せぬシステムエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
Resume CleanUp
End Sub

‘ ==============================================================================
‘ 処理名:ProcessSingleProjectFile
‘ 概要 :単一の.mppファイルを開き、処理を行って安全に閉じる(関心事の分離)
‘ 引数 :filePath (String) – 対象ファイルのフルパス
‘ 戻り値:Boolean – 成功時True
‘ ==============================================================================
Private Function ProcessSingleProjectFile(ByVal filePath As String) As Boolean
Dim targetProject As Project
Dim openSuccess As Boolean

ProcessSingleProjectFile = False

On Error GoTo FileErrorHandler

‘ A. 安全なファイルオープン(読み取り専用推奨の無視、ダイアログ抑制)
‘ FileOpenEx メソッドを使用し、割り込み要素を極力排除する
openSuccess = Application.FileOpenEx( _
Name:=filePath, _
ReadOnly:=False, _
IgnoreReadOnlyRecommended:=True _
)

If Not openSuccess Then
Debug.Print “ファイルのオープンに失敗しました: ” & filePath
Exit Function
End If

‘ B. 開いたプロジェクトオブジェクトの安全な取得
‘ ActiveProject ではなく、開いた直後のオブジェクトを参照領域に特定する
Set targetProject = Application.ActiveProject

‘ ————————————————————————–
‘ C. 【実務処理領域】ここに各プロジェクトに対する自動化コードを記述する
‘ ————————————————————————–
‘ 例:全タスクのメモに処理タイムスタンプを付与するダミー処理
‘ Call ExecuteBusinessLogic(targetProject)
‘ ————————————————————————–

‘ D. 保存して閉じる(メモリの確実な解放)
‘ FileCloseEx を使用し、保存オプションとUI不問を指定
Application.FileCloseEx Save:=pjSave, NoPrompt:=True

‘ E. 明示的な参照の破棄(COMオブジェクトのライフサイクル終了)
Set targetProject = Nothing

ProcessSingleProjectFile = True
Exit Function

FileErrorHandler:
‘ ファイル個別のエラーログをイミディエイトウィンドウに出力し、全体ループは止めない
Debug.Print “エラー発生 [” & filePath & “] : ” & Err.Number & ” – ” & Err.Description

‘ エラー時も開いてしまったファイルの後始末を試みる
On Error Resume Next
If Not targetProject Is Nothing Then
Application.FileCloseEx Save:=pjDoNotSave, NoPrompt:=True
Set targetProject = Nothing
End If
On Error GoTo 0

ProcessSingleProjectFile = False
End Function

4. プロダクションコードの極意:アーキテクチャ解説

提供したコードは、現場での実用に耐えうるよう設計されています。重要な設計ポイントを解説します。

① `FileOpenEx` と `FileCloseEx` の厳格な採用

MS Projectには `FileOpen` と `FileOpenEx` が存在します。プロダクション環境では必ず `FileOpenEx` を使用してください。
`IgnoreReadOnlyRecommended:=True` パラメータを与えることで、「読み取り専用で開くことを推奨します」というダイアログを強制的にバイパスできます。閉じる際も `FileCloseEx Save:=pjSave, NoPrompt:=True` を指定することで、保存確認ダイアログの割り込みを完全に遮断します。

② エラーの局所化(Local Error Handling)

ループ全体を1つの `On Error` で包むのはアンチパターンです。3番目のファイルでエラーが起きた際、残りの97件の処理を諦めることになります。
コード例では `ProcessSingleProjectFile` という関数にエラーハンドリングを閉じ込め、1ファイルの失敗が全体ループを停止させない設計にしています。

③ COMオブジェクト参照の明示的破棄 (`Set targetProject = Nothing`)

VBAのガベージコレクションは頼りになりません。特にMS ProjectのCOMコンポーネントは、参照が1つでも残っているとバックグラウンドで `.mpp` ファイルのロックを保持し続けます。処理が終わった瞬間に `Set … = Nothing` で明示的に参照カウントをゼロに落とすのが鉄則です。

5. エントラプライズ開発における追加の注意点

ツールをさらにスケールさせる場合、以下の点に配慮してください。

1. ネットワークドライブ・OneDriveの同期遅延

対象ファイルがOneDriveやSharePointと同期されたローカルフォルダにある場合、`Dir` 関数が開閉の衝撃で同期処理と衝突し、ファイルロック例外(Err 70)を投げることがあります。一括処理を行う際は、「ローカルの作業用テンポラリフォルダに一括コピーしてから処理し、終わったら書き戻す」アーキテクチャを検討してください。

2. データベース(SQL Server / Excel等)連携時の注意

一括処理の中でデータベースやExcelへ進捗ログを吐き出す場合、ファイルが開閉するたびにDB接続を開閉(`Open` / `Close`)させないでください。接続コネクションは `BatchProcessMPPFiles`(親側)で1つ保持し、子関数に参照を渡すことで、I/Oボトルネックを劇的に削減できます。

結び:コードの「堅牢さ」が自動化の価値を決める

初心者向けの解説では「とりあえず動くコード」が提示されがちですが、実務の現場で求められるのは「1000個のファイルを放置して夜間実行しても止まらないコード」です。

今回紹介した「画面描画の抑制」「ダイアログの無効化」「例外処理の分離」「明示的なオブジェクト解放」をテンプレートとして活用し、プロジェクト管理の業務効率化を次のレベルへと引き上げてください。

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