【テクニカル・上級編】タスクの「制約条件」を無視して依存関係を強制的に優先させる一括設定スクリプト – Project VBA解析バイブル

スポンサーリンク

【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はレガシーな言語と揶揄されることがあるが、オブジェクトモデルの挙動とアプリケーションのライフサイクルを完全に掌握していれば、巨大なプロジェクト管理プラットフォームを意のままに操る最強の武器となる。現場の非効率を、コードの力で根絶やしにせよ。

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