【実務・中級編】リソースの「カレンダー」を個別のプロジェクトファイルから一括削除するクリーンアップ – Project VBA解析バイブル

スポンサーリンク

【Project VBA極限知見】肥大化したMSPを救え!リソースカレンダー一括クリーンアップの極意

プロジェクトマネージャーなら誰もが一度は直面する恐怖がある。
何世代もの計画変更を経て、ファイルサイズが数MBから数十MBへ膨れ上がり、保存や再計算のたびにPCがフリーズするかのような沈黙を保つMicrosoft Project(MSP)ファイル。

その元凶の多くは、ガントチャートのタスクではない。「リソースカレンダーのゴミ(ゾンビ化例外日)」だ。

今回は、Project VBAのオブジェクトモデルの深淵に踏み込み、個別のプロジェクトファイルに巣食う不要なリソースカレンダーの例外日を検出し、一網打尽にクリーンアップするプロダクションコードを授けよう。

—

なぜMSPファイルは肥大化するのか?(非効率な現実)

MSPでは、リソースに対して「ベースカレンダー」を割り当て、さらに個別の「例外日(稼働時間変更や休日)」を設定できる。
実務において、過去の変更履歴やアドホックなスケジュール調整を繰り返すうちに、リソースカレンダーには「もう誰も使っていない過去の例外日」や「重複したゴミデータ」が無限に蓄積されていく。

UI上からこれを一つひとつ手作業で削除するなど、エンジニアの恥だ。
しかし、VBAでこれを雑に実装すると、「参照整合性エラー」や「無限ループ」、「最悪の場合のファイル破損」を引き起こす。

我々が目指すべきは、「安全かつアトミックに、不要なリソース例外日をパージする堅牢なメカニズム」である。

—

堅牢なクリーンアップ設計の3原則

実務でそのまま使えるツールを構築するため、以下の設計思想をコードに落とし込む。

1. ベースカレンダーの保護

  • 「標準」「夜勤」などのグローバル/ベースカレンダーを誤って破壊してはならない。ターゲットはあくまで「リソース個別に紐づく非標準の例外日」または「未使用のカレンダー」である。

2. トランザクション的思考(エラーハンドリング)

  • 処理途中で予期せぬ例外が発生した場合、ファイルを中途半端な状態で上書き保存してはならない。

3. オブジェクトのライフサイクルを意識した走査

  • `Resource.Calendar` オブジェクトのコレクションを安全に列挙し、削除判定を行う。`For Each` の中でコレクションを直接いじるとメモリリークやインデックスズレの元となる。

—

【コピペ即稼働】リソースカレンダー・クリーンアップマクロ

以下のコードを、ProjectのVBAエディタ(`Alt + F11`)の標準モジュールに貼り付けて実行してほしい。
実務の現場で耐えうるよう、詳細なログ出力と厳格なエラーハンドリングを実装している。

Option Explicit

‘ ==============================================================================
‘ 処理名 : CleanResourceCalendars
‘ 概要 : アクティブなプロジェクト内の全リソース固有カレンダーから、
‘ 過去の不要な例外日をパージし、ファイルを軽量化する。
‘ 著者 : チーフアーキテクト
‘ ==============================================================================
Public Sub CleanResourceCalendars()
Dim prj As Project
Set prj = ActiveProject

‘ 実行確認(誤操作防止)
Dim response As VbMsgBoxResult
response = MsgBox(“現在のプロジェクト [” & prj.Name & “] のリソースカレンダーを最適化します。” & vbCrLf & _
“この操作により、過去の不要な例外日が削除されます。実行しますか?”, _
vbYesNo + vbExclamation, “リソースカレンダー・クリーンアップ”)
If response = vbNo Then Exit Sub

‘ 画面描画を停止してパフォーマンスを極限まで引き上げる
App.ScreenUpdating = False
On Error GoTo ErrorHandler

Dim res As Resource
Dim cal As Calendar
Dim exc As Exception
Dim deletedCount As Long
Dim targetDate As Date

deletedCount = 0
targetDate = Date – 365 ‘ 例として「1年以上前の例外日」をパージ対象とする

Dim msgLog As String
msgLog = “— クリーンアップ処理開始 —” & vbCrLf

‘ プロジェクト内の全リソースを走査
For Each res In prj.Resources
‘ リソースが有効(Nullでない)かつ、独自のカレンダーを持っている場合のみ処理
If Not res Is Nothing Then
If res.Name <> “” Then
‘ リソースに紐づくカレンダーを取得
‘ ※MSPでは各リソースは独自のCalendarオブジェクトを持つ
Set cal = Nothing
On Error Resume Next
Set cal = res.Calendar
On Error GoTo ErrorHandler

If Not cal Is Nothing Then
‘ ベースカレンダー以外(リソース固有のカレンダー)を対象とする
If Not cal.BaseCalendar Then

‘ カレンダー内の例外日(Exceptions)を逆順で走査(削除時のインデックスズレを防ぐため)
Dim i As Long
For i = cal.Exceptions.Count To 1 Step -1
Set exc = cal.Exceptions(i)

‘ 【削除ロジック】設定された期日より古い例外日を削除
‘ ※業務要件に合わせてこの条件式は変更すること
If exc.Finish < targetDate Then msgLog = msgLog & "削除: リソース[" & res.Name & "] の例外日 (" & exc.Start & " ~ " & exc.Finish & ")" & vbCrLf exc.Delete deletedCount = deletedCount + 1 End If Next i End If End If End If End If Next res ' 終了処理 App.ScreenUpdating = True MsgBox "クリーンアップが完了しました。" & vbCrLf & _ "削除された古い例外日: " & deletedCount & " 件", vbInformation, "完了" Debug.Print msgLog Exit Sub ErrorHandler: ' 異常終了時のリカバリ App.ScreenUpdating = True MsgBox "エラーが発生しました: " & Err.Description, vbCritical, "致命的なエラー" ' 必要に応じてログ出力やロールバック処理をここに記述 End Sub ---

チーフアーキテクトからの実務アドバイス

1. `App.ScreenUpdating = False` の重要性
MSPのVBAにおいて、UIの再描画は最も重い処理の一つだ。リソース数やタスク数が多い大規模プロジェクトでは、これを挟むだけで実行速度が数倍〜十数倍に跳ね上がる。
2. 逆順ループ (`Step -1`) の鉄則
コレクション(この場合は `Exceptions`)の要素を削除する際、先頭から回すとインデックスが狂い、必ずバグ(実行時エラーや削除漏れ)を引き起こす。「コレクションの削除は必ず末尾から逆順で回す」。これはVBAプログラミングにおける絶対の教義である。
3. ファイル連携時の注意点
サーバー上のProject ServerやProject Online(PWA)環境に接続している場合、リソース情報はグローバル(エンタープライズリソースプール)側で管理されていることが多い。エンタープライズリソースのカレンダーをローカルマクロで勝手に書き換えると、組織全体に整合性エラーをばらまくことになる。本コードは、スタンドアロンの `.mpp` ファイル、またはローカルリソースプールを持つファイルに限定して使用せよ。

技術の本質を理解し、無駄を削ぎ落としたコードこそが、あなたのプロジェクトを救う。現場のエンジニア諸君の健闘を祈る。

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