Project VBAの深淵:依存関係の再構築アルゴリズムとクリティカルパスの最適化
MS ProjectのVBAを単なる「マクロ記録の延長」と捉えている者は、この強力なスケジューリングエンジンの真の姿を見誤っている。Projectは単なるタスク管理ソフトではない。それは、無数の制約条件が相互に作用する「複雑系シミュレーションエンジン」である。
本稿では、タスク削除時に依存関係(先行タスク)を壊さず、クリティカルパスを最適化し続けるための「依存関係再接続アルゴリズム」の極致を解説する。
—
1. オブジェクトモデルの罠:なぜ `TaskLinks` はメモリを食うのか
Projectのオブジェクトモデルにおいて、`Task`オブジェクトと`TaskLink`オブジェクトの相関関係は、一見単純に見えて極めて揮発性が高い。タスクを削除する際、単に `Task.Delete` を叩くのは素人の所業だ。
先行タスク(Predecessors)は、プロジェクト全体のクリティカルパスを構成する「動脈」である。これを断絶させれば、スケジュールは崩壊する。メモリリークとオブジェクト参照の不整合を回避する唯一の道は、参照の明示的解放と、コレクションの反復処理における「逆順操作」の徹底にある。
必須の作法:オブジェクトの明示的破棄
VBAのガベージコレクションは頼りにならない。大量のタスクを操作する際は、必ず `Set obj = Nothing` をループの最後で行うこと。また、`Application.ScreenUpdating = False` は必須だが、さらに `Calculate` メソッドの呼び出しタイミングを制御し、再計算負荷を最小化せよ。
—
2. 依存関係維持・再接続のアルゴリズム
特定のタスク `TargetTask` を削除する際、そのタスクが持つ「先行タスク」と「後続タスク」を直接接続するロジックを実装する。
実装コード:`SafeRemoveTask` アルゴリズム
Public Sub SafeRemoveTask(ByVal targetTask As MSProject.Task)
Dim predecessors As MSProject.TaskDependencies
Dim successors As MSProject.TaskDependencies
Dim predTask As MSProject.Task
Dim succTask As MSProject.Task
Dim proj As MSProject.Project
Set proj = targetTask.Project
‘ 1. 先行・後続タスクのコレクションを取得
Set predecessors = targetTask.PredecessorTasks
Set successors = targetTask.SuccessorTasks
‘ 2. 先行タスクを後続タスクに再接続(依存関係のブリッジ)
‘ 注意: ここでLinkを生成しないとクリティカルパスが分断される
Dim p As Object, s As Object
For Each p In predecessors
For Each s In successors
‘ 重複リンクを避けるためのチェック
If Not IsLinked(p, s) Then
proj.TaskLinks.Add From:=p, To:=s, LinkType:=pjFinishToStart
End If
Next s
Next p
‘ 3. オブジェクトの明示的解放(パフォーマンスの最適化)
Set predecessors = Nothing
Set successors = Nothing
‘ 4. タスク削除
targetTask.Delete
‘ 5. 再計算の強制(必要に応じて)
proj.Calculate
End Sub
‘ リンク存在チェック(極めて重要)
Private Function IsLinked(ByVal fromTask As Task, ByVal toTask As Task) As Boolean
Dim tl As TaskLink
For Each tl In fromTask.SuccessorTaskLinks
If tl.ToTask.ID = toTask.ID Then
IsLinked = True
Exit Function
End If
Next tl
IsLinked = False
End Function
—
3. レガシー環境とパフォーマンスへの警鐘
もしあなたがエンタープライズ環境の数千行規模のスケジュールを相手にしているなら、`Application.Calculate` を安易にループ内で実行してはならない。これはプロセッサを枯渇させる要因となる。
極限の知見:最適化のヒント
- 計算モードの制御: 大規模な変更を加える際は、`Application.Calculation = pjManual` に設定し、すべてのリンク操作が完了した後に一度だけ `Calculate` を呼び出すこと。
- Windows APIの活用: `Task` オブジェクトのプロパティ参照が極端に遅い場合、`Win32 API` を用いてメモリ上のデータ構造を直接叩くことも視野に入るが、これはOSのバージョン依存が激しいため、最後の手段とせよ。
- IDの変動に注意: `Task.ID` は削除に伴いインデックスが再割り当てされる。`UniqueID` をキーにして辞書(`Scripting.Dictionary`)を構築し、マッピングを保持するのがシニアエンジニアの流儀である。
—
結論:システムアーキテクトとしての自覚
VBAはレガシーと言われるが、MS Projectの内部エンジン(Pj.exe)は未だに強力なバイナリ・プロトコルを保持している。我々が書くコードは、単なるマクロではなく、プロジェクトの「命運」を操作するスクリプトである。
依存関係を壊さず、クリティカルパスを最適化し続けるこのアルゴリズムを、君たちのライブラリのコアに組み込んでほしい。論理的整合性を欠いた自動化は、いずれ「負の遺産」としてシステムを崩壊させる。常に、メモリの隅々まで意識を張り巡らせよ。
健闘を祈る。
