【テクニカル・上級編】【上級プロ】Taskの「ConstraintType」を動的に変更した際のスケジュール再計算をVBAで制御する – Project VBA解析バイブル

スポンサーリンク

Project VBAを掌握する極限の知見:Taskの「ConstraintType」動的変更とスケジュール再計算の完全制御

Microsoft ProjectのVBA自動化において、最もエンジニアの頭を悩ませるのが「スケジュール計算エンジン(Scheduler Engine)の非同期・即時性に関する挙動」である。

特に、数千行規模の大規模工程表において、`Task.ConstraintType`(制約の種類)や`ConstraintDate`をプログラムから一括変更した際、MS Projectの内部エンジンがどのようにスケジュールを再計算し、メモリ上でどう整合性を保っているかを知る者は少ない。

本稿では、レガシーなVBA環境からMS ProjectのCOMオブジェクトモデルの深層に切り込み、制約変更時のスケジュール崩壊を防ぎ、極限までパフォーマンスを引き出すための制御技術を解説する。

1. MS Projectスケジューリングエンジンの深層と「遅延評価」の罠

MS Projectのオブジェクトモデルにおいて、`Task`オブジェクトのプロパティを変更した瞬間、即座にプロジェクト全体のCPM(クリティカルパス法)に基づく再計算が走ると考えているなら、それは致命的な誤りだ。

MS Projectはパフォーマンス最適化のため、一連のプロパティ変更に対する再計算を内部キューに溜め込む「遅延評価(Lazy Evaluation)」の挙動を示す。これに起因して以下のトラブルが頻発する。

  • 値の不整合: `ConstraintType`を `pjConstraintMso`(可能な限り遅く)に変更した直後に `Start` 日付を参照しても、再計算が完了しておらず古い値が返る。
  • 無限ループ・デッドロック的挙動: 連続するタスク群に対してループ内で制約と日付をバラバラに書き換えると、内部計算エンジンが衝突し、タスクの順序が逆転してガントチャートが「崩壊」する。

これを防ぐためには、「手動計算モードへの切り替え」「トランザクション的な一括処理」「計算エンジンの明示的な強制実行」の3ステップをVBAで完全に制御しなければならない。

2. 実装アーキテクチャ:安全かつ高速なConstraint一括変更エンジン

以下に、数千件のタスクを持つプロジェクトであっても、スケジュールを崩さずに安全かつ高速に `ConstraintType` を変更するプロダクション品質のVBAコードを提示する。

Option Explicit

‘ Windows API: 処理の合間にUIのフリーズを防ぎつつメッセージキューを処理する
If VBA7 Then
Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
End If

Public Sub OptimizeConstraintTransition()
Dim prj As MSProject.Project
Dim tsk As MSProject.Task
Dim originalCalcMode As Long

Set prj = ActiveProject

‘ ==========================================
‘ Phase 1: 環境の退避とグローバル計算の停止
‘ ==========================================
‘ 自動再計算を停止し、予期せぬエンジン暴走を防ぐ
originalCalcMode = prj.Calculation
prj.Calculation = pjManual

‘ 画面描画を停止し、COMインターフェースのオーバヘッドを極限まで削減
App.ScreenUpdating = False

On Error GoTo ErrorHandler

‘ ==========================================
‘ Phase 2: トランザクション処理(メモリ上の操作)
‘ ==========================================
Dim counter As Long
counter = 0

For Each tsk In prj.Tasks
‘ 削除済みタスクやサマリータスクの例外処理
If Not tsk Is Nothing Then
If Not tsk.Summary Then

‘ 例:特定の条件に基づき制約タイプを動的変更
If tsk.Milestone Then
‘ マイルストーンは「指定日以降」に変更
tsk.ConstraintType = pjConstraintSnet
‘ 必要に応じてConstraintDateも同期させる
‘ tsk.ConstraintDate = tsk.Start
Else
‘ 通常タスクは「できる限り早く」
tsk.ConstraintType = pjConstraintAsap
End If

counter = counter + 1

‘ 巨大プロジェクトにおけるCOMメモリ溢れを防ぐため適度に解放
If counter Mod 500 = 0 Then
DoEvents
End If
End If
End If
Next tsk

‘ ==========================================
‘ Phase 3: 計算エンジンの強制同期と再計算
‘ ==========================================
‘ 計算モードを戻す
prj.Calculation = originalCalcMode

‘ プロジェクト全体のスケジュールを強制再計算 (CalculateProject)
prj.Calculate

‘ 正常終了処理
GoTo CleanUp

ErrorHandler:
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “Critical Architecture Error”

CleanUp:
‘ ==========================================
‘ Phase 4: 資源の確実な解放と環境復元
‘ ==========================================
App.ScreenUpdating = True

‘ オブジェクト変数の明示的破棄(メモリリーク防止)
Set tsk = Nothing
Set prj = Nothing

MsgBox “制約条件の動的変更とスケジュール再計算が正常に完了しました。”, vbInformation, “System Complete”
End Sub

3. チーフアーキテクトが解説するコードの急所

上記のコードには、レガシーVBAとMS Project連携の現場で培われた幾重もの防衛策が組み込まれている。

① `prj.Calculation = pjManual` によるスケジューリングの凍結

タスクを1つずつループで書き換えるたびにプロジェクト全体を再計算させていると、$O(N^2)$ に近い計算オーダーとなり、数千タスク規模では処理が数十分〜数時間でフリーズする。計算を `Manual` に落とすことで、メモリ上のデータ構造のみを高速に書き換える。

② `prj.Calculate` による一括評価

書き換えがすべて完了した後に、`prj.Calculation` を元のモード(通常は `pjAutomatic`)に戻し、最後に `prj.Calculate` を明示的に叩く。これにより、MS Projectのコアエンジンは依存関係グラフを一括して評価し、一瞬で正しいクリティカルパスと日付を導き出す。

③ COMオブジェクトの明示的解放とメモリ管理

VBAはガベージコレクションの挙動が曖昧である。特に `For Each` ループ内で `MSProject.Task` を参照し続けると、COMラッパーがメモリ上に残存し、ExcelやProjectといったホストアプリケーションのメモリリーク(VBA特有の「Automation Error」の温床)を引き起こす。
ループを抜けた後、あるいは処理の節目でオブジェクト変数に `Nothing` を代入する作法は、エンタープライズ領域では必須である。

4. システム間連携(外部DB/API)における応用

この「制約変更と計算制御」のテクニックは、基幹システム(SAPやERPなど)から取得した最新の進捗・制約データをMS Projectに流し込むバッチ処理において真価を発揮する。

外部連携時は、JSONやCSVからパースしたデータをメモリ上で高速にマッピングし、上記のように計算エンジンを停止した状態で一気に流し込む。最後に `prj.Calculate` を実行し、整合性が保証されたスケジュールをPDFやMSPファイルとして出力するパイプラインを構築することで、ヒューマンエラーとパフォーマンスのボトルネックを完全に排除できる。

VBAはレガシーな言語と揶揄されがちだが、その裏にあるアプリケーションのライフサイクルとメモリモデルを完全に掌握していれば、現代のモダンな自動化基盤に対抗しうる強靭なシステムを構築可能である。妥協なきコードで、プロジェクトの命運を握るスケジュールを完全に支配せよ。

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