鎮魂と再構築:Project VBAにおける「制約再計算」を極限まで加速させる技術
Project VBA(Microsoft Projectのオブジェクトモデル)を扱う際、多くの開発者が直面する最大の壁は「再計算のオーバーヘッド」だ。プロジェクト開始日を1日ずらしただけで、数百のタスクが依存関係の連鎖反応(クリティカルパスの再走査)を引き起こし、IDEが応答なしになる。
これは単なる「遅い」という問題ではない。イベント駆動型のプログラミングにおいて、「計算エンジンがいつ、どの範囲で動いているか」を制御できていないことの証明である。
今日は、その「再計算の泥沼」から脱却し、コンマ数秒の世界でタスク群を再配置するアーキテクチャの真髄を伝授する。
—
1. 計算エンジンの「強制停止」という選択
MS Projectのオブジェクトモデルは、デフォルトで強力な「制約計算エンジン」が裏で動いている。タスクの `Start` プロパティを書き換えるたびに、エンジンは即座に依存関係をチェックする。
これを防ぐための鉄則は、「計算エンジンを完全に沈黙させた状態で、メモリ上のデータを一括更新し、最後に一度だけ再計算させる」ことだ。
実践:Calculationの排他制御
以下のコードは、Applicationオブジェクトの計算モードを切り替え、エンジンを殺した状態で全タスクを更新する定石である。
‘ 計算エンジンを制御するラッパー関数
Public Sub OptimizeProjectUpdate(newStartDate As Date)
Dim proj As Project
Set proj = ActiveProject
‘ 1. 計算エンジンを一時停止(これが生命線)
Dim originalCalc As PjCalculation
originalCalc = Application.Calculation
Application.Calculation = pjManual
On Error GoTo Cleanup
‘ 2. メモリ上での高速更新処理
‘ オブジェクトアクセスを最小化するため、必要ならTaskコレクションを配列に吸い出す
UpdateTaskSchedule proj, newStartDate
‘ 3. 再計算を実行し、モードを戻す
Application.Calculation = originalCalc
proj.Recalculate ‘ ここで初めて全タスクの制約が確定される
Exit Sub
Cleanup:
Application.Calculation = originalCalc
MsgBox “更新中にエラーが発生しました”, vbCritical
End Sub
—
2. メモリ・オブジェクトアクセスの極限最適化
VBAの遅延の正体は、COMインターフェースを介した「プロパティへの過剰なアクセス」にある。`Task.Start` をループ内で叩くたびに、VBAランタイムとProjectエンジン間を往復するマーシャリングコストが発生している。
もし数千件規模のタスクを扱うなら、以下の手法を検討せよ。
- Withステートメントの強制:
`Task.Start` を `With` で括るだけで、メモリ参照のポインタ解決を最適化できる。
- イベントの完全遮断:
`Application.EnableEvents = False` を併用すること。これは、再計算によってトリガーされる不要なChangeイベントを抑制し、スレッドを保護する。
—
3. Windows APIによる「思考停止」の回避
大規模プロジェクトで `Recalculate` を呼ぶと、OSレベルで「応答なし」と判定されることがある。これはWindowsがメッセージキューを処理できなくなるからだ。
ここで、`DoEvents` を多用するのは素人だ。`DoEvents` はパフォーマンスを殺す。代わりに、Windows APIの `Sleep` を最小限に制御し、必要に応じて `PeekMessage` でメッセージキューをクリアするのが、シニアエンジニアの流儀である。
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
‘ 長大な処理の合間に実行し、OSへの応答を維持する
Public Sub KeepAlive()
‘ わずか1ミリ秒の解放で、OSは「ハングアップしていない」と判断する
Sleep 1
End Sub
—
4. シニアエンジニアへの提言:なぜ「再計算」は重いのか
ASAP(可能な限り早く)やALAP(可能な限り遅く)といった制約条件は、グラフ理論で言う「トポロジカルソート」を全タスクに対して行う必要がある。
- 制約の極小化: 可能な限り「固定日(Must Start On)」を避けること。依存関係(FS: Finish-to-Start)だけで組まれたプロジェクトは、計算コストが線形に近い。しかし、制約が複雑に絡むと計算量は指数関数的に増大する。
- 再計算のスコープ: もし可能なら、プロジェクト全体ではなく、特定のサマリータスク配下のみを更新する運用を設計段階で組み込め。
結びに代えて
VBAはレガシーと言われるが、そのアーキテクチャの根底にあるCOM/ActiveXの理解は、現代のクラウドネイティブなAPI設計にも通じるものがある。「何が裏で動いているか」を想像できる人間だけが、システムを限界まで追い込める。
ツールはただ使うものではない。支配するものである。
このコードが、君のプロジェクトを「応答なし」の地獄から解放することを願っている。
