ASAP制約の罠:Project VBAにおける計算エンジン競合の「解体新書」
Project VBAを扱う際、多くのエンジニアが直面する壁がある。それが「ASAP(As Soon As Possible)制約の暴走」だ。
タスクの開始日をプログラムから強制的に固定しようとした瞬間、Projectの計算エンジンがバックグラウンドで火を噴き、後続タスクやリンク情報と衝突を起こす。結果、意図しない日付への再計算が発生し、ガントチャートは無残な姿に成り果てる。
なぜこうなるのか。理由は明確だ。Projectの計算エンジンは、オブジェクトのプロパティが変更されるたびに、即座に依存関係の再計算を試みるからだ。
今回は、この計算エンジンを「物理的に制圧」し、大規模なスケジュール更新を安全かつ高速に完遂するための、極限のアーキテクチャを伝授する。
—
1. なぜ「ASAP」は裏切るのか
ASAP制約とは、実質的に「制約なし(Start No Earlier Than の最小値)」と同義だ。しかし、VBAで`Task.Start`を書き換える行為は、計算エンジンに対して「ユーザーによる手動変更」というトリガーを引く。
もしプロジェクト全体で自動再計算(CalculationMode)がオンのままであれば、一つのタスクを書き換えるたびに、依存関係にある全タスクが再計算される。数千行規模のスケジュールにおいて、これをループ処理で回せば、計算コストは指数関数的に増大し、メモリリークの温床となる。
解決の鍵:計算エンジンの「強制停止」
これを防ぐための唯一の解は、処理の間、計算エンジンを完全に「沈黙」させることだ。
—
2. 実装:計算エンジンを制御する「アーキテクトのコード」
以下は、大規模なスケジュール更新を行う際の定石となるテンプレートだ。重要なのは `Calculation` プロパティの制御と、オブジェクトの確実な解放である。
‘ — プロジェクト更新の最適化モジュール —
Option Explicit
Public Sub ApplyTaskDatesOptimized(targetProject As Project)
Dim t As Task
Dim originalCalculationMode As Long
‘ 1. 計算エンジンの状態を退避し、一時停止させる
‘ これを行わないと、1タスク書き換えるたびに再計算が走る
originalCalculationMode = targetProject.Calculation
targetProject.Calculation = pjManual
On Error GoTo Cleanup
‘ 2. メモリ効率を意識したループ処理
For Each t In targetProject.Tasks
If Not t Is Nothing Then
‘ 制約をASAPに設定し、開始日を強制固定する
‘ ConstraintType: pjConstraintASAP = 0
t.ConstraintType = pjConstraintASAP
t.Start = “2023/10/01 08:00”
End If
Next t
‘ 3. 再計算を許可し、一括で整合性を取らせる
targetProject.Calculation = originalCalculationMode
Cleanup:
‘ 4. オブジェクトの明示的解放(VBAのメモリ管理は信頼しすぎないこと)
Set t = Nothing
If Err.Number <> 0 Then
MsgBox “Critical Error: ” & Err.Description, vbCritical
targetProject.Calculation = originalCalculationMode
End If
End Sub
—
3. レガシー環境における「メモリ最適化」の真実
大規模なプロジェクトファイル(数万タスク)を扱う際、`For Each` ループ内でオブジェクト参照を頻繁に行うと、VBAのガベージコレクションが追いつかず、Project自体がフリーズ(ハングアップ)することがある。
シニアエンジニアが守るべき3つの鉄則
1. Late Bindingの回避:
`Dim t As Task` と型を指定せよ。`Object` 型による動的バインディングは、VBAの実行効率を大幅に低下させる。
2. イベントの無効化:
もし `Project_Change` イベントが実装されている場合、`Application.EnableEvents = False` を忘れずに行うこと。これを怠ると、計算エンジンの停止とイベントの無限ループが競合し、システムがスタックする。
3. API呼び出しによる強制更新:
どうしても計算エンジンが整合性を取れない複雑な依存関係がある場合、Windows APIの `SendMessage` を使用して、再描画の強制更新をかけるのが最終手段だ。
‘ Windows APIで画面描画を停止し、再描画を制御する例
Private Declare PtrSafe Function LockWindowUpdate Lib “user32” (ByVal hwndLock As LongPtr) As LongPtr
‘ 処理開始前
LockWindowUpdate Application.hWnd
‘ 処理終了後
LockWindowUpdate 0
—
終わりに:アーキテクトとしての矜持
VBAは「簡易的な自動化ツール」ではない。Project VBAは、数千億規模のプロジェクトマネジメントを背負うエンジンの一部なのだ。
計算エンジンを敵視するのではなく、その挙動を先回りして制御する。制約条件がどのように評価されるかを理解し、メモリとCPUのコストを最小化する。これこそが、レガシー環境でも揺るぎないシステムを構築する「伝説のアーキテクト」の所作である。
あなたの書くコードが、今日も誰かのプロジェクトを救うことを期待している。
