【テクニカル・上級編】プロジェクト開始日変更時の全タスク制約再計算をVBAで高速化するテクニック – Project VBA解析バイブル

スポンサーリンク

鎮魂と再構築: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設計にも通じるものがある。「何が裏で動いているか」を想像できる人間だけが、システムを限界まで追い込める。

ツールはただ使うものではない。支配するものである。
このコードが、君のプロジェクトを「応答なし」の地獄から解放することを願っている。

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