【実務・中級編】計算エンジンを強制的に再起動させる:VBAによるプロジェクト再計算のトリガー実装 – Project VBA解析バイブル

スポンサーリンク

Project VBAの暗部を暴く:計算エンジンの「強制再計算」を制御せよ

Project VBAを扱っていて、最もフラストレーションが溜まる瞬間はどこか?
それは、「コードで値を書き換えたのに、ガントチャートやクリティカルパスが更新されない」という現象に直面した時だろう。

MS Projectの計算エンジンは、パフォーマンス最適化のために内部キャッシュを優先する設計になっている。そのため、VBAから`Task.Start`や`Task.Duration`を叩いても、エンジンが「まだ計算の必要なし」と判断すれば、UI上の表示やスケジュール計算が後回しにされる。

本記事では、この「計算エンジンのサボり」を叩き起こし、確実に再計算を完遂させるためのアーキテクチャを伝授する。

—

1. なぜ「CalculateAll」だけでは不十分なのか

多くの初学者は、とりあえず `Application.CalculateAll` をループの最後に記述する。だが、これは「巨大なプロジェクトファイルに対する爆弾」になり得る。

  • 副作用の温床: `CalculateAll` はプロジェクト全体を走査する。大規模なスケジュールでこれをループ内に入れると、計算コストが指数関数的に増大し、処理時間が数分から数時間へ跳ね上がる。
  • 非同期の罠: プロジェクトの計算エンジンは非同期的に動作することがある。`CalculateAll`を呼び出した直後に次のプロパティを参照しようとすると、古い値を取得してしまうケースがある。

我々が目指すべきは、「必要なタイミングで、最小限のコストで再計算を強制する」ことだ。

—

2. 堅牢な再計算トリガーの実装コード

単なるメソッド呼び出しではなく、計算完了を待機し、エラーをハンドリングする堅牢なラッパーを作成する。

‘ プロジェクト計算エンジンの実行を管理するクラスモジュール的アプローチ
Public Sub ExecuteRobustRecalculate(ByRef proj As Project)
On Error GoTo ErrorHandler

‘ 1. 計算エンジンが既に動いている場合は一旦静止を待つ
‘ 頻繁な再計算リクエストによるハングアップを防ぐ
If proj.IsCalculatePending Then
‘ 必要に応じてログを出力
Debug.Print “Pending calculation detected. Syncing…”
End If

‘ 2. アプリケーションの計算モードを一時的に制御(パフォーマンス対策)
Dim originalMode As PjCalculation
originalMode = Application.Calculation
Application.Calculation = pjManual ‘ 自動計算を切ることで意図しない再計算連鎖を防ぐ

‘ 3. 強制再計算の実行
‘ CalculateAllは非常に強力だが、プロジェクトの整合性が取れている状態で呼び出すこと
proj.CalculateAll

‘ 4. モードを戻す
Application.Calculation = originalMode

Exit Sub

ErrorHandler:
MsgBox “計算エンジンの同期に失敗しました: ” & Err.Description, vbCritical
Application.Calculation = originalMode
End Sub

この設計のポイント

  • `pjManual`への切り替え: プログラムによる一括更新中に自動計算が走ると、整合性が取れない中間状態で再計算が行われ、ガントチャートが崩壊するリスクがある。更新中は必ず手動モードにし、最後に一括で計算させるのが鉄則だ。
  • エラーハンドリング: 再計算中に循環参照や制約条件の矛盾が発生すると、エンジンは例外を投げる。これを握りつぶさず、ログとして出力する設計が必要だ。

—

3. データベース連携時の「整合性」という難問

外部DB(SQL ServerやAzure DevOps等)からデータをProjectに流し込む際、もっとも注意すべきは「制約条件の違反」である。

例えば、`ASAP(できるだけ早く)`制約のタスクに、過去の日付を強引に書き込めば、Projectは自動的に計算エンジンを介して日付を調整しようとする。この時、プログラムの意図した値と、エンジンが勝手に調整した値の間で乖離が生まれる。

解決策:制約条件のクリアと再設定

更新処理を行う際は、以下の手順を徹底すること。

1. 制約条件の解除: `Task.ConstraintType = pjConstraintNone` に設定し、一旦自由な状態にする。
2. 値の更新: 日付や期間を書き込む。
3. 再計算の実行: `ExecuteRobustRecalculate` を呼び出す。
4. 制約の再設定: 必要に応じて `pjConstraintStartNoEarlierThan` 等を適用する。

この手順を踏まない限り、DBの正当性は担保できない。

—

4. 伝説のアーキテクトからの助言

最後に一つ、忘れてはならないことがある。
「計算エンジンを強制的に回しすぎるな」。

頻繁な `CalculateAll` は、Projectのメモリリークを誘発する。特に、外部アドインや複雑なカスタムフィールドが多数設定されているプロジェクトファイルでは、計算エンジンが無限ループに陥るリスクすらある。

計算は「更新プロセスの最後」に一度だけ行う。これが、大規模プロジェクトをVBAで制御するための唯一の解法だ。

あなたの書くコードが、計算エンジンという「ブラックボックス」の気まぐれに翻弄されるのではなく、あなたの制御下にあることを確信してほしい。健闘を祈る。

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