Project VBAを掌握する極限の知見
プロジェクト開始日の変更に伴う全タスクの「開始日固定」制約の自動解除と再配置
プロジェクトマネジメントにおいて、スケジュール全体のスライドは日常茶飯事だ。しかし、Project VBAを用いて大規模なWBSを制御する際、最もエンジニアを絶望させるのが「固定制約(ConstraintType)」の亡霊である。
プロジェクトの開始日が変更されたため、全体を後ろ倒ししようとした矢先、一部のタスクに `msoConstraintStartNoEarlierThan` などの固定制約がハードコード(あるいはユーザーの誤操作)で残っているがために、スケジュールの再計算(`CalculateGlobal` / `Calculate`)が部分的に無視され、ガントチャートがグチャグチャに崩壊する——。あなたもこの悪夢に直面したことがあるはずだ。
今回は、この現場の地獄をロジカルに解決するための、堅牢で破壊力のあるVBA自動化ロジックを伝授する。
—
なぜ愚直なループ処理では破綻するのか?(非効率な設計の排除)
多くの初学者は、プロジェクト内の全タスクを `For Each` で回し、単純に制約プロパティを書き換えるコードを書く。
‘ 【アンチパターン】絶対にやってはいけない実装
Dim t As Task
For Each t In ActiveProject.Tasks
t.ConstraintType = pjConstraintAsSoonAsPossible
t.ConstraintDate = t.Start ‘ ← これでは意味がない
Next t
このアプローチが実務で通用しない理由は3つある。
1. オブジェクトモデルのオーバーヘッド: VBAとMS ProjectのCOM間通信は重い。タスクが数千件ある場合、個別のプロパティ変更を逐次行うと、画面描画と再計算が走ってフリーズ寸前の遅延を生む。
2. サマリータスク(要約タスク)の例外処理: サマリータスクは子タスクの集計値で動くべきだが、ここに無闇に制約をかけると、MS Projectのスケジューリングエンジンと競合しエラーを吐く。
3. リンク(依存関係)との整合性: 制約を外した瞬間に先行タスクとのリンク(Predecessors)による自動計算が走り、意図せぬ日付にジャンプすることがある。
真にプロフェッショナルなエンジニアは、「イベントの抑制」「バッチ処理的な一括操作」「例外の厳格なハンドリング」を組み合わせる。
—
プロダクションコード:全タスクの制約解除と再配置エンジン
以下のコードは、実務の現場でそのまま組み込めるエンタープライズグレードのVBAモジュールだ。新しいプロジェクト開始日を受け取り、全タスクの邪魔な固定制約を「できる限り早く(ASAP)」に強制変更した上で、安全にスケジュールを再計算させる。
Option Explicit
‘ ==============================================================================
‘ モジュール名: icsProjectScheduleManager
‘ 概要 : プロジェクト開始日の変更に伴う全タスクの制約解除と自動再配置
‘ 著者 : Chief Project VBA Architect
‘ ==============================================================================
Public Sub ShiftProjectSchedule(ByVal newProjectStart As Date)
Dim prj As Project
Set prj = ActiveProject
‘ 1. パフォーマンス最大化と予期せぬ描画エラーの防止
Application.ScreenUpdating = False
On Error GoTo ErrorHandler
Dim originalCalculation As Long
originalCalculation = Application.Calculation
Application.Calculation = pjManual ‘ 計算モードを一時手動に
Dim t As Task
Dim unlockedCount As Long
unlockedCount = 0
MsgBox “プロジェクト開始日を ” & Format(newProjectStart, “YYYY/MM/DD”) & ” に変更し、制約を解除します。”, vbInformation, “処理開始”
‘ 2. プロジェクト自体の開始日を更新
prj.ProjectStart = newProjectStart
‘ 3. 全タスク走査と制約のパージ
For Each t In prj.Tasks
If Not t Is Nothing Then
‘ サマリータスクやマイルストーン、外部タスクの例外を考慮
If Not t.Summary And Not t.ExternalTask Then
‘ 固定制約(ASAP/ALAP以外)がかかっている場合のみ処理
Select Case t.ConstraintType
Case pjConstraintStartNoEarlierThan, pjConstraintStartNoLaterThan, _
pjConstraintFinishNoEarlierThan, pjConstraintFinishNoLaterThan, _
pjConstraintMustStartOn, pjConstraintMustFinishOn
‘ 制約を「できる限り早く (ASAP)」に変更
t.ConstraintType = pjConstraintAsSoonAsPossible
‘ 制約日付をクリア(ゼロ日付に)
t.ConstraintDate = #1/1/1984#
unlockedCount = unlockedCount + 1
End Select
End If
End If
Next t
‘ 4. 計算モードを戻し、全体を強制再計算
Application.Calculation = originalCalculation
prj.Calculate
Application.ScreenUpdating = True
MsgBox “正常に完了しました。” & vbCrLf & _
“解除された固定制約タスク数: ” & unlockedCount & ” 件”, vbInformation, “処理完了”
Exit Sub
ErrorHandler:
‘ 異常終了時の環境復旧
Application.ScreenUpdating = True
Application.Calculation = originalCalculation
MsgBox “重大なエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
End Sub
—
現場で陥りがちな罠:データベース連携・外部ファイル運用の注意点
この自動化ロジックを、Excelからのデータインポートや、外部データベース(SQL Server / SharePoint等)と連携するシステムに組み込む際、以下の2点に細心の注意を払う必要がある。
1. カレンダーと稼働日(Working Days)の同期:
開始日をスライドさせた際、土日祝日や独自のカレンダー例外(企業の創立記念日など)を無視して日付を計算させると、タスクの期間(Duration)が意図せずズレる。VBAで日付を操作する前に、MS Project側のカレンダー設定が最新の企業マスタと同期されていることを前提条件(Prerequisite)としなければならない。
2. リソースの過負荷(Resource Overallocation)の検知:
制約を外してスケジュールを前に詰め直した結果、特定のリソースにタスクが集中し、過負荷状態に陥ることが多々ある。必要であれば、上記のコードの直後に `prj.LevelNow` メソッドを呼び出し、リソース平準化(Leveling)のロジックをチェインさせると完璧なエンドツーエンドの自動化が構築できる。
結びにかえて
VBAは単なる「マクロの記録」の延長ではない。アプリケーションのオブジェクトモデルの挙動を完全に把握し、エンジン(MS Projectのスケジューラー)の意図通りに調教するための高度なシステム開発言語である。
「固定制約の暴走」という現場のボトルネックをこのコードで完全に駆逐し、真に動的なWBS管理基盤をあなたのプロジェクトに実装してほしい。
