Project VBAの深淵:タスク依存関係「ラグタイム」一括制御の極意
Project VBA(Microsoft Project Object Model)を触る際、多くのエンジニアが陥る罠がある。それは「GUIの操作をそのままコードに落とし込む」という愚策だ。
GUIで「FSリンクのラグを-2日に変更する」操作をマクロ記録すれば、`TaskDependencies`を巡回して値を書き換えるコードができる。だが、数千行のプロジェクトでそれをやれば、イベント駆動のオーバーヘッドでツールはフリーズし、不整合なデータが残る。
真の自動化エンジニアは、「オブジェクトのライフサイクル」と「再計算エンジンへの負荷」を制御する。 今日は、プロジェクト管理の現場で最も工数を食う「依存関係のラグタイム一括調整」を、堅牢かつ高速に完遂するためのアーキテクチャを伝授する。
—
1. なぜ「力技」ではいけないのか
依存関係(Predecessors)は、単なるプロパティの集合ではない。`TaskDependency`オブジェクトは、プロジェクトの再計算エンジンと密接に結びついている。
- ループ更新の罠: タスクを1つ更新するたびにプロジェクト全体が再計算(Recalculation)を試みると、ツールは指数関数的に遅くなる。
- 不整合の温床: リンクが存在しないタスクや、循環参照が発生するような無理なラグ設定を強行すると、プロジェクトファイルは容易に破損する。
- 例外処理の欠如: `TaskDependency`が削除された瞬間にイテレータが崩壊するリスクを考慮しないコードは、実務では「爆弾」でしかない。
—
2. 堅牢な一括調整ツールの設計思想
以下のコードは、単にラグを一括変更するだけではない。「安全な反復処理」と「パフォーマンス最適化」を両立させたプロダクション・コードだ。
実装コード:`AdjustTaskLags` プロシージャ
‘ プロジェクトの全タスクのFSリンクに対し、特定のルールでラグを一括適用する
‘ @param targetLagDays: 設定したいラグ日数(負数なら前倒し)
Public Sub BatchUpdateTaskLags(ByVal targetLagDays As Long)
Dim tsk As Task
Dim dep As TaskDependency
Dim proj As Project
Set proj = ActiveProject
‘ 1. パフォーマンス向上のため、一時的に自動再計算を抑制(大規模時)
‘ 注意: 処理終了後に必ず戻すこと
Application.Calculation = pjManual
On Error GoTo ErrorHandler
For Each tsk In proj.Tasks
If Not tsk Is Nothing Then
‘ 2. 依存関係のイテレータを安全に走査
For Each dep In tsk.TaskDependencies
‘ FSリンク(Finish-to-Start)のみを対象とするのが定石
If dep.Type = pjFinishToStart Then
‘ 3. ラグの単位は通常「分」で保持されているため換算
‘ Projectの既定の分換算(1日=480分が一般的)
dep.Lag = targetLagDays 480
End If
Next dep
End If
Next tsk
MsgBox “ラグの一括調整が完了しました。”, vbInformation
ExitPoint:
Application.Calculation = pjAutomatic
Exit Sub
ErrorHandler:
MsgBox “エラー発生: ” & Err.Description, vbCritical
Resume ExitPoint
End Sub
—
3. 実務で生き残るための「3つの知見」
① ラグタイムの単位(Minutes)を理解せよ
Project VBAの`Lag`プロパティは「分(Minutes)」単位で格納されている。`dep.Lag = 2` と書いても、それは2分間を意味する。`480`(8時間)を乗算するこの処理は、プロジェクトの稼働カレンダーと整合性を取るための必須の作法だ。
② 再計算エンジンの制御(`Calculation`プロパティ)
数千行規模のプロジェクトでは、`Application.Calculation = pjManual` を挟むか否かで、処理時間が数分から数秒へ激変する。ただし、手動モードにしたまま処理を終了させると、ユーザーが次の更新で「反映されない」と混乱するため、必ずエラーハンドリング内で自動計算に戻すこと。
③ データのクリーンアップと整合性チェック
もし「削除」を行う場合は、`dep.Delete` を使用する。しかし、`For Each` ループ内でオブジェクトを削除するとポインタが外れる。削除を行う場合は、必ず「後ろから前へ(`For i = tsk.TaskDependencies.Count To 1 Step -1`)」ループさせるのが定石だ。
—
結論:ツールは「道具」ではなく「知性」である
このツールは、単なる工数削減の道具ではない。プロジェクトマネージャーが「もしこのタスクが予定通り終わらなかったら?」というシミュレーションを瞬時に行うための、意思決定支援システムだ。
VBAコードを書くことは、プロジェクトの「呼吸」をコード化することに近い。今回紹介したコードは、あくまでテンプレートだ。貴方の現場のプロジェクトカレンダーやカスタムフィールドの運用に合わせて、さらにこのアーキテクチャを洗練させてほしい。
開発者諸君、GUIの表面をなぞるだけのコードは捨てろ。オブジェクトの深層を理解し、プロジェクトの脈動を制御せよ。それができる者だけが、真の自動化エンジニアと名乗れるのだから。
