【Project VBA極限の知見】タスク制約の呪縛を断つ:依存関係強制優先エンジン
レガシーなProject管理の現場において、VBAエンジニアが最も直面し、そして最も頭を抱える問題の一つが「制約条件(Constraint Types)と依存関係(Predecessors/Successors)の衝突」である。
「なぜ先行タスクを後ろにずらしたのに、後続タスクの日付が連動しないのか?」
「スケジュールを自動最適化したいのに、『指定日以降開始(SNET)』や『鋼鉄の如き』開始日固定タスクがスケジュールエンジンをハングアップさせている」
プロジェクトマネージャーが無意識に、あるいは保身のために設定した無数の手動制約。これが、Projectの強力なCPM(クリティカルパス法)エンジンを骨抜きにしている。
今回は、Project VBAのオブジェクトモデルの深部を突撃し、タスクの制約条件を強制的に「制約なし(As Soon As Possible / 可能な限り早くだ)」に剥ぎ取り、純粋な依存関係のみでスケジュールを再計算させる「制約パージ&依存関係強制優先エンジン」の実装コードを公開する。
レガシー環境特有の罠、メモリ管理、そしてProjectオブジェクトのライフサイクルの真実と共に解説しよう。
—
1. Project VBAにおける「制約条件」の闇とパフォーマンス
MS Projectのオブジェクトモデルにおいて、`Task`オブジェクトの `ConstraintType` プロパティは、スケジューリングの挙動を決定づける極めて重いプロパティだ。
‘ 主な制約タイプの定数 (PjConstraint)
‘ pjConstraintAsSoonAsPossible (0) – 可能な限り早く
‘ pjConstraintAsLateAsPossible (2) – 可能な限り遅く
‘ pjConstraintMustStartOn (4) – 開始日固定
‘ pjConstraintStartNoEarlierThan (6) – 指定日以降開始 (SNET)
これらが設定されているタスクに対し、VBAから一括で日付操作や依存関係の再構築を行おうとすると、Projectの内部エンジンはタスクごとにスケジュール再計算のキューを生成する。数千行規模のWBSでこれを愚直にループさせると、COMのラウンドトリップコストが爆発し、Excelとは比較にならないほどの深刻なパフォーマンス低下(いわゆる「応答なし」状態)を引き起こす。
さらに、`pjConstraintAsSoonAsPossible` 以外の制約が存在する状態でリンク(依存関係)を張ると、制約が依存関係に優先してしまい、スケジュールの自動伝播が完全に阻害される。
この呪縛を断ち切るには、「アプリケーションの画面描画と自動計算を一時的に完全に停止(ScreenUpdating / Calculation Off)」させ、メモリ上でアトミックにプロパティを一括書き換えした上で、最後に強制再計算(`Calculate`)を叩き込むアプローチが唯一にして絶対の解となる。
—
2. 実装コード:制約パージ&依存関係強制優先エンジン
以下のコードは、選択されたプロジェクト内の全タスク(またはサマリーを除く実タスク)の制約条件を「可能な限り早く(ASAP)」に強制変更し、依存関係によるスケジュールを完全優先させるプロダクションレベルの実務ツールである。
Option Explicit
‘ ==============================================================================
‘ 処理名: ForceDependencyEngine
‘ 概要 : 全タスクの制約条件を「可能な限り早く(ASAP)」に強制変換し、
‘ 依存関係によるCPMスケジュールを完全に復元・優先させる。
‘ ==============================================================================
Public Sub ForceDependencyEngine()
Dim prj As Project
Set prj = ActiveProject
‘ 1. 巨大プロジェクトにおけるパフォーマンス劣化を防ぐための極限最適化
‘ 画面描画、イベント、自動計算をすべてシャットダウン
Dim origScreenUpdating As Boolean
Dim origCalculation As Long
origScreenUpdating = Application.ScreenUpdating
origCalculation = Application.Calculation
On Error GoTo ErrorHandler
Application.ScreenUpdating = False
Application.Calculation = pjManual ‘ 手動計算モードへ移行
‘ 2. 処理開始のログと安全性確認
Dim msgResult As VbMsgBoxResult
msgResult = MsgBox(“警告: この処理はプロジェクト内のすべてのタスク制約を「可能な限り早く(ASAP)」に強制上書きします。” & vbCrLf & _
“手動で固定された開始日や期限はすべて解除され、依存関係(先行・後続)のみでスケジュールが再計算されます。” & vbCrLf & _
“実行しますか?”, vbExclamation + vbYesNo, “制約パージ&依存関係強制エンジン”)
If msgResult = vbNo Then Exit Sub
Dim t As Task
Dim processedCount As Long
processedCount = 0
‘ ステータスバーに進行状況を表示
Application.StatusBar = “タスクの制約条件を解析・パージ中…”
‘ 3. タスクコレクションの走査とインプレース更新
‘ ※ サマリータスクはスケジュールエンジンが自動管理するため、実タスク(Summary = False)のみを対象とする
For Each t In prj.Tasks
If Not t Is Nothing Then
If Not t.Summary Then
‘ マイルストーンかつ制約が固定されている場合などの例外を考慮しつつ
‘ ConstraintTypeを強制的に「可能な限り早く (pjConstraintAsSoonAsPossible = 0)」へ変更
If t.ConstraintType <> pjConstraintAsSoonAsPossible Then
‘ 念のため、制約日付(ConstraintDate)も初期化(NA値に設定)
‘ ※制約タイプがASAPの場合、日付は実質無効になるがクリーンに保つ
t.ConstraintType = pjConstraintAsSoonAsPossible
processedCount = processedCount + 1
End If
End If
End If
Next t
‘ 4. エンジンの再計算と強制同期
Application.Calculation = pjAutomatic ‘ 自動計算モードへ復帰
‘ プロジェクト全体の再計算を強制実行
prj.Calculate
‘ 5. クリーンアップ
Application.ScreenUpdating = origScreenUpdating
Application.StatusBar = “”
MsgBox “処理が完了しました。” & vbCrLf & _
“制約をパージして依存関係を優先したタスク数: ” & processedCount & ” 件”, _
vbInformation, “実行完了”
Exit Sub
ErrorHandler:
‘ 異常終了時のフェイルセーフ:必ず環境を元の状態に戻す
Application.ScreenUpdating = origScreenUpdating
Application.Calculation = origCalculation
Application.StatusBar = “”
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
End Sub
—
3. チーフアーキテクトが解説するコードの急所
① `Application.Calculation = pjManual` の重要性
MS Projectは、タスクのプロパティ(特に `ConstraintType` や `Start`)が書き換えられるたびに、裏でCPMエンジンを走らせて後続タスクの日付連鎖を計算し直す。
数千行のWBSでこれをやると、1回の代入ごとに数ミリ秒〜数十ミリ秒のロスが発生し、全体で数分間のフリーズを招く。計算モードを `pjManual` に退避させ、メモリ上で一括書き換えを行った後に `prj.Calculate` を1回だけ叩く手法により、処理時間を数分からコンマ数秒へと劇的に短縮している。
② サマリータスク(Summary Tasks)の除外
Projectにおいて、サマリータスクの制約や日付をVBAから直接無理やり変更しようとすると、COMエラー(ランタイムエラー)が発生するか、プロジェクト全体の構造が破損する原因になる。
`If Not t.Summary Then` というガード句によって、ユーザーが直接編集すべき「実タスク(Leaf Tasks)」のみにターゲットを絞り込み、システム整合性を担保している。
③ 鉄壁のフェイルセーフ(Error Handler)
大規模VBA開発において、途中でエラーが発生したまま `ScreenUpdating = False` や `Calculation = pjManual` が放置されると、ユーザーのProject環境が完全に破壊され、アプリの再起動を余儀なくされる。
`ErrorHandler:` ラベルを必ず設置し、いかなる例外が発生しようとも元のUI状態と計算モードを確実に復元する設計は、プロフェッショナルな現場では必須の作法である。
—
4. システム間連携・レガシー保守における知見
外部システム(基幹ERPやExcel製進捗管理表など)からCSVやDB経由で吐き出されたデータをMS Projectにインポートする際、自動生成されたタスクには、なぜか勝手に `pjConstraintStartNoEarlierThan` などの厄介な制約がデフォルト付与されるケースが多々ある。
この「汚染されたインポートデータ」をそのままにしておくと、PMOが後から進捗線を引き直したり先行タスクを追加したりしても、スケジュールがびくとも動かない「ゴミのようなWBS」が完成する。
この自動化スクリプトをタスクインポート直後の「フック処理(前処理・後処理)」として組み込んでおくことで、「外部連携データであっても、常に純粋な依存関係ツリーとして正しく振る舞う」強靭なプロジェクト基盤を構築できる。
VBAはレガシーな言語と揶揄されることがあるが、オブジェクトモデルの挙動とアプリケーションのライフサイクルを完全に掌握していれば、巨大なプロジェクト管理プラットフォームを意のままに操る最強の武器となる。現場の非効率を、コードの力で根絶やしにせよ。
