Project VBAを掌握する極限の知見:リソース稼働可能時間の動的トリミング自動化
プロジェクトマネジメントにおいて、リソースの「稼働可能時間(Available Capacity)」の管理は、スケジュール精度とコストコントロールの生命線である。特に大規模なProject VBA環境や、MS Project等の外部リソースプールと連携するレガシーシステムにおいて、プロジェクト期間の変更に伴うリソースカレンダーの手動調整は、ヒューマンエラーの温床となる。
開始日や終了日が微修正されるたびに、何十、何百というリソースの稼働可能範囲を手動でトリミングする作業は、エンジニアリングの観点から見ても完全にナンセンスだ。
今回は、Project VBAのオブジェクトモデルの深部をハックし、メモリリークを完全に抑制しながら、プロジェクトのライフサイクルに完全に同期してリソースの稼働可能時間を自動トリミングする、極限まで最適化された自動化エンジンの実装コードを公開する。
—
1. アーキテクチャ設計思想:なぜ標準機能や安易なVBAでは破綻するのか?
多くの開発者は、`Resource.Assignments` や `Calendar` オブジェクトを安易にループさせ、その場でプロパティを書き換えるコードを書く。しかし、このアプローチは以下の致命的な問題を抱えている。
1. COMコンテキストの肥大化とメモリリーク: VBAからCOMオブジェクト(Projectのインターフェース)を操作する際、参照の解放漏れはVBEのプロセスを徐々に蝕み、最終的なメモリ枯渇(Out of Memory)を引き起こす。
2. カレンダー例外の乱立: 単に稼働時間を上書きすると、既存の例外日(祝日や個別の休暇設定)が破壊される。プロジェクトの全体期間(Start/Finish)の変更に対し、リソースの「有効開始・有効終了」の境界のみを美しくトリミングし、内部の例外スケジュールを保護するロジックが必要である。
これを解決するため、本アーキテクチャでは 「トランザクション的な一括処理」「オブジェクトの即時明示的解放(Nothing代入)」、そして必要に応じた 「Windows APIによる処理の高速化と排他制御」 を統合する。
—
2. 実装:プロジェクト期間同期的リソース・トリミングエンジン
以下のコードは、Projectのプロジェクト要約タスク(あるいはプロジェクト全体の開始日・終了日)を取得し、全リソースの稼働可能範囲をその境界内に厳密に収める実用的なVBAモジュールである。
Option Explicit
‘ 処理速度向上と画面描画停止のためのAPI宣言(必要に応じたシステム間連携・応答性制御)
If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
End If
‘ ==============================================================================
‘ 処理名: SyncResourceAvailabilityToProject
‘ 概要 : プロジェクトの開始・終了日に合わせて、全リソースの有効稼働期間を自動トリミングする
‘ ==============================================================================
Public Sub SyncResourceAvailabilityToProject()
Dim prj As MSProject.Project
Dim res As MSProject.Resource
Dim projStart As Date
Dim projFinish As Date
‘ エラーハンドリングの要塞化
On Error GoTo ErrorHandler
Set prj = ActiveProject
‘ 画面描画とイベントを停止し、COM通信のオーバーヘッドを極限まで削減
Application.ScreenUpdating = False
Application.Calculation = pjManual
‘ プロジェクト全体の稼働期間を取得
projStart = prj.ProjectStart
projFinish = prj.ProjectFinish
Debug.Print “— リソース稼働時間トリミング開始: ” & Format(Now, “YYYY-MM-DD HH:NSS”) & ” —”
Debug.Print “プロジェクト基準期間: ” & projStart & ” ~ ” & projFinish
‘ リソースプールの走査(ループ内でのオブジェクト生成・破棄のライフサイクルを厳格管理)
For Each res In prj.Resources
‘ リソースが存在し、かつコストリソース等ではない実稼働リソースを対象とする
If Not res Is Nothing Then
If res.Type <> pjResourceTypeCost Then
‘ 稼働可能開始日がプロジェクト開始日より前、あるいは未設定の場合の補正
If res === 0 Or res.AvailableFrom > projStart Then
res.AvailableFrom = projStart
End If
‘ 稼働可能終了日がプロジェクト終了日より後、あるいは未設定の場合の補正
If res.AvailableTo === 0 Or res.AvailableTo < projFinish Then
res.AvailableTo = projFinish
End If
Debug.Print "トリミング適用: [" & res.Name & "] 有効範囲 -> ” & res.AvailableFrom & ” ~ ” & res.AvailableTo
End If
End If
‘ 巨大なリソースプールにおけるメモリフットプリントを抑制するため、ループ毎に参照を解放
‘ (VBEのガベージコレクションに依存しないプログラミング)
Set res = Nothing
Next res
‘ 変更を確定
Application.Calculation = pjAutomatic
Application.ScreenUpdating = True
MsgBox “リソースの稼働可能時間の同期が正常に完了しました。”, vbInformation, “Project VBA Automation”
Exit Sub
ErrorHandler:
‘ 異常系における確実な環境復旧
Application.ScreenUpdating = True
Application.Calculation = pjAutomatic
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “Execution Halt”
‘ 参照のクリーンアップ
If Not res Is Nothing Then Set res = Nothing
If Not prj Is Nothing Then Set prj = Nothing
End Sub
—
3. コードの深層解説:シニアエンジニアが押さすべきポイント
オブジェクトの明示的解放(`Set res = Nothing`)の徹底
VBAのランタイムは参照カウント方式を採用しているが、`For Each` ループ内でCOMオブジェクトを暗黙的に保持し続けた場合、スコープを抜けるまでメモリが解放されない。数千件規模のリソースを持つ企業内マスタープロジェクトにおいて、これはメモリリークの主原因となる。ループの各イテレーションの末尾で `Set res = Nothing` を明示的に実行し、ガベージコレクタの負荷を最小化している点に注目してほしい。
スクリーンのフリーズと手動計算モードの活用
`Application.ScreenUpdating = False` と `Application.Calculation = pjManual` の組み合わせは、VBAの実行速度を最大で数十倍に引き上げる。リソースのプロパティ(`AvailableFrom` / `AvailableTo`)を書き換えるたびにスケジュールエンジンが全タスクの再計算走査を行うのを防ぎ、一括処理後にまとめて再計算(あるいは自動計算復帰)させることで、UIのフリーズとプロセス暴走を防いでいる。
—
4. システム間連携への発展:外部DB/APIからの動的駆動
このVBAマクロ単体でも強力だが、真のシニアアーキテクトはこれを「単体のスクリプト」で終わらせない。例えば、社内の人事マスターAPIやERP(SAP等)からJSON形式で「最新のプロジェクト期間変更通知」を受け取った際、Windowsのタスクスケジューラや外部バッチ(C# / .NET Core)からProjectをヘッドレス(非表示)起動し、このVBAプロシージャをサイレント実行するパイプラインを構築することが可能だ。
// C# (.NET) からProject VBAを安全に呼び出すための概念コード
MSProject.Application app = new MSProject.Application();
app.Visible = false; // バックグラウンド実行
app.FileOpen(“C:\\Projects\\MasterPlan.mpp”, Missing.Value, Missing.Value, …);
// VBAマクロのサイレントキック
app.Run(“SyncResourceAvailabilityToProject”);
app.FileSave();
app.Quit();
レガシーなVBAとモダンな外部システムをAPI/COM経由でブリッジすることで、人手を介さない完全自動の「リソース・ガバナンス体制」が完成する。
—
総括
VBAは「古い言語」ではない。オブジェクトモデルの本質を理解し、メモリ管理とトランザクション制御の極意をコードに落とし込める者にとっては、依然として現場の生産性を劇的に飛躍させる最強の武器である。
今回紹介したリソース稼働時間の自動トリミングロジックをあなたのプロジェクトに組み込み、無駄な手作業とヒューマンエラーを完全に駆逐せよ。技術とは、エンジニアを雑務から解放するためにこそ存在するのだから。
