演算の深淵を制御せよ:MS Project スケジューリングエンジンの挙動を掌握する極限のアーキテクチャ
MS Projectを単なる「ガントチャート作成ツール」と考えているエンジニアが、大規模プロジェクトの自動化に挑むとき、必ずと言っていいほど計算の泥沼に足を取られる。
数千、数万のタスクが複雑な依存関係(Predecessors/Successors)の網の目の中に閉じ込められているとき、一箇所の制約(Constraint)を変更しただけで、エンジンは全ネットワークの再計算を開始する。この「再計算の連鎖」こそが、自動化スクリプトのパフォーマンスを著しく低下させ、最悪の場合、COMオブジェクトのデッドロックやメモリリークを引き起こす原因となる。
本稿では、単にタスクのプロパティを書き換えるレベルを超え、Projectの心臓部である「スケジューリングエンジン」の計算タイミングと制約条件を、VBAを通じていかに「外科手術的」に制御するかを解説する。これは、大規模なレガシーシステムを保守し、あるいは高信頼性の自動化ツールを設計するシニアエンジニアのための知見である。
1. 計算エンジンのライフサイクル管理:`Calculation` プロパティの真実
多くの開発者は、ループ内でタスクの開始日や期間を書き換える。しかし、`Application.Calculation` が `pjAuto`(自動計算)に設定されている状態でこれを実行するのは、火薬庫の中で火花を散らすようなものだ。
タスクを1つ更新するたびに、エンジンは依存関係のグラフ(Dependency Graph)を走査し、クリティカルパスを再計算する。計算量が $O(n^2)$ に近い挙動を示すことも珍しくない。
プロフェッショナルの戦略:バッチ更新モデル
エンジニアリングの鉄則は、「状態の変化を最小限の回数に集約し、計算を一度だけ実行する」ことだ。
' ---
' スケジュール更新の最適化モジュール
' ---
Public Sub OptimizedBatchUpdate(ByRef targetProject As MSProject.Project)
On Error GoTo ErrorHandler
' 1. 計算モードを「手動」に切り替え、エンジンの暴走を阻止する
' これにより、各プロパティ変更時の再計算コストをゼロにする
Application.Calculation = pjManual
' 2. 描画更新の停止(UIのオーバーヘッドを削減)
' ※ProjectにはExcelのScreenUpdatingに相当する直接のプロパティがないが、
' 最小化状態で処理を行うことで描画負荷を軽減できる
Dim originalWindowState As Integer
originalWindowState = Application.WindowState
Application.WindowState = pjMinimized
' --- [メインロジック開始] ---
' ここで大量のタスク操作を行う
' 例: 全タスクの制約条件を一時的に緩和する、あるいは期間を一括更新する
Dim t As MSProject.Task
For Each t In targetProject.Tasks
If Not t Is Nothing Then
' 制約条件を ASAP (As Soon As Possible) に強制変更する例
' これにより、計算の衝突(Constraint Conflict)を回避しやすくなる
t.ConstraintType = pjAsap
End If
Next t
' --- [メインロジック終了] ---
' 3. 最後に一括して再計算を実行する
' これがエンジンの「計算重み」を最も効率的に処理するタイミングである
Application.Calculate
' 4. 状態を復元
Application.Calculation = pjAuto
Application.WindowState = originalWindowState
MsgBox "Batch update completed successfully.", vbInformation
Exit Sub
ErrorHandler:
' エラー発生時でも必ず計算モードを自動に戻す(これを忘れるとProjectが死ぬ)
Application.Calculation = pjAuto
Application.WindowState = originalWindowState
MsgBox "Critical Error: " & Err.Description, vbCritical
End Sub
2. 制約条件(Constraints)とASAP/ALAPの動的制御
スケジューリングエンジンの挙動を決定づけるのは、`ConstraintType` である。
シニアエンジニアなら、単に「日付を入れる」のではなく、「エンジンの制約をどう解釈させるか」を設計しなければならない。
- `pjAsap` (As Soon As Possible): エンジンに完全な自由を与える。依存関係のみに基づいた計算。
- `pjAlap` (As Late As As Possible): プロジェクトの終端から逆算する。リソース平準化において極めて重要。
- `pjMustStartOn` / `pjMustFinishOn`: エンジンに対する「命令」であり、依存関係よりも優先される。
大量のタスクを流し込む際、あらかじめ全てのタスクを `pjAsap` に設定し、計算を完了させた後に、特定の重要なマイルストーンに対してのみ `pjMustStartOn` を適用する。この「緩和から制約へ」というシーケンスこそが、計算エンジンの衝突を回避する定石である。
3. Windows APIによる「ゴースト現象」の回避とメモリ管理
大規模な自動化処理中、Projectのウィンドウが「応答なし」の状態になることがある。これは計算エンジンがシングルスレッドでグラフの走査に没頭しているためだ。
また、COMオブジェクトを介した連携(ExcelやAccessからの操作)では、オブジェクトの解放が不完全だと、メモリ上に「ゾンビプロセス」が残り続ける。
Windows APIによるプロセス制御と堅牢な解放
' --- API宣言部 ---
If VBA7 Then
Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Declare PtrSafe Function GetForegroundWindow Lib "user32" () As LongPtr
Else
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Declare Function GetForegroundWindow Lib "user32" () As Long
End If
' ---
' オブジェクトのライフサイクルを管理する高度な解放ルーチン
' ---
Public Sub SecureRelease(ByRef obj As Object)
On Error Resume Next
If Not obj Is Nothing Then
' オブジェクトがCOM参照を保持している可能性があるため、
' 明示的にNothingを代入し、参照カウントを減らす
Set obj = Nothing
End If
On Error GoTo 0
End Sub
' 実行中の計算負荷を監視するための疑似的な待機
Public Sub YieldProcessing()
' 計算エンジンに制御を一時的に戻し、OSのメッセージキューを処理させる
DoEvents
Sleep 10 ' 10msの猶予を与える
End Sub
アーキテクトの視点:メモリの整合性
VBAで `Set task = Project.Tasks(i)` とした際、この `task` オブジェクトはProjectアプリケーションのメモリ空間へのポインタである。ループの終わりで `Set task = Nothing` を行わない場合、スコープを抜けた後も参照カウントが残るリスクがある。特に、外部アプリケーション(C#等)からCOM経由で呼び出す場合、この「参照の残留」はメモリリークの主因となる。
4. システム間連携における極限の知見:COM Automationの罠
もし君が、VB.NETやC#からProjectを制御する「システム管理者」であるならば、以下の事実に留意せよ。
1. Late Binding vs Early Binding:
速度と型安全性のトレードオフを理解せよ。大規模なループ内では `Early Binding`(参照設定)が必須だが、配布するツールにおいては `Late Binding` による互換性確保が必要になる。
2. Error Handlingの階層化:
Projectの計算エラー(循環参照など)は、VBAの `Err` オブジェクトでは捕捉しきれないことがある。計算エンジンが内部的に「不整合」を検知した際の挙動を、APIレベルで監視する構えが必要だ。
結論
MS ProjectのVBA操作において、真のプロフェッショナルは「タスクのプロパティ」を見ているのではない。その背後で動いている「計算エンジンの計算コスト」と「オブジェクトの生存期間」を制御しているのだ。
`Calculation = pjManual` による計算の集約、制約条件の戦略的な緩和、そしてWindows APIを用いたプロセスの安定化。これらを組み合わせることで、初めて数万タスク規模のエンタープライズ・スケジューリングを、堅牢かつ高速に制御することが可能になる。
技術の深淵へようこそ。ここからは、コードの背後にある「重み」を感じ取ってほしい。
