MS Project VBAを掌握する極限の知見:WBSの呪縛を断ち切れ!タスク制約条件を一括クリーンアップする自動化アーキテクチャ
プロジェクトマネージャーの机から引き継がれた、あるいは現場のメンバーが我流で継ぎ足したMS Project(mpp)ファイルを開いた瞬間、冷や汗をかいたことはないだろうか。
ガントチャートのバーが勝手に固定され、ロジカルなはずの先行・後続タスクの依存関係(FS、SSなど)を無視して暴走するスケジュール。その元凶のほとんどは、無秩序に設定された「タスクの制約条件(Constraint Type)」にある。
「指定日以降に開始」「できる限り遅く」といった特殊な制約がWBSの深部に潜んでいると、どれだけCPM(クリティカルパス法)を信じてスケジュールを最適化しようとも、Projectのエンジンは歪んだ計算結果を吐き出し続ける。
今回は、このプロジェクトの癌とも言える不正な制約条件を根絶やしにし、すべてのタスクを標準的な「できるだけ早く(ASAP: As Soon As Possible)」へと強制リセット、あるいは意図したマイルストーン制約へと一括コンバートする、実務直結のプロダクションVBAコードを授けよう。
—
1. なぜ「GUIの手動操作」では破綻するのか
数百、数千行に及ぶWBSに対して、ユーザーが一つひとつタスクを選択し、情報を確認して制約を「できるだけ早く」に変更していく……そんな非効率な作業を許容する現場は、もはやエンジニアリングを放棄していると言っていい。
さらに、MS ProjectのCOMオブジェクトモデルは、Excelのそれとは比較にならないほど「イベント駆動と再計算のオーバーヘッド」が重い。
画面を描画させたままでループ処理を回そうものなら、Windowsのタスクマネージャーが悲鳴を上げ、最悪の場合はCOM例外(エラー 11001 等)で沈没する。
バグの起きない堅牢なツールを作るためには、以下の3つの鉄則を死守しなければならない。
1. 画面描画(ScreenUpdating)と自動計算(Calculation)の完全封印
2. オブジェクトのライフサイクルと解放(特に`Task`と`Assignments`のメモリ管理)
3. 「固定日」や「マイルストーン」など、意図的に維持すべき制約の例外処理
—
2. アーキテクチャ設計:安全かつ高速なクリーンアップロジック
今回構築するプロシージャの心臓部は、プロジェクト内の全タスクを走査し、サマリータスク(親タスク)や外部タスクといった「触ってはならない聖域」をバイパスしつつ、実務上無効化すべき制約を安全に書き換える仕組みだ。
特に注意すべきは、MS Projectにおける「制約の種類(`ConstraintType`)」の列挙体(`PjConstraint`)の挙動である。
単純にすべてをASAPにすると、ガチガチに固定したい「完了期限(MSO: Must Finish On)」や「指定日終了(FNET: Finish No Earlier Than)」まで破壊してしまう。したがって、「どの制約は許容し、どの制約を強制排除するか」のビジネスロジックをコード内に明確に定義する必要がある。
—
3. プロダクションVBAコード
以下のコードを、MS Project側のVBAエディタ(`ThisProject` または標準モジュール)に貼り付けて実行してほしい。実務の現場で即座に耐えうる、エラーハンドリングとパフォーマンスチューニングを施した極限のコードだ。
Option Explicit
‘ =================================================================================
‘ módulo名: ModConstraintCleaner
‘ 概要: プロジェクト内の全タスクの制約条件をスキャンし、指定ルールに基づき一括最適化する
‘ 著者: シニアVBAアーキテクチャエンジニア
‘ =================================================================================
Public Sub CleanAndNormalizeTaskConstraints()
Dim prj As Project
Set prj = ActiveProject
‘ 実行確認(誤操作防止)
Dim confirmMsg As VbMsgBoxResult
confirmMsg = MsgBox(“現在のプロジェクト(” & prj.Name & “)に含まれる全タスクの制約条件を” & vbCrLf & _
“「できるだけ早く (ASAP)」を基本形にクリーンアップします。” & vbCrLf & _
“※意図的なマイルストーン制約等を除くすべてのカスタム制約が上書きされます。よろしいですか?”, _
vbYesNo + vbExclamation, “タスク制約一括クリーンアップ”)
If confirmMsg = vbNo Then Exit Sub
‘ —————————————————————————–
‘ 1. パフォーマンス・アクセラレーション(描画・自動計算の停止)
‘ —————————————————————————–
Dim originalCalcMode As PjCalculation
originalCalcMode = Application.Calculation
Application.Calculation = pjManual ‘ 計算を手動モードに固定
Dim startTime As Double
startTime = Timer
Dim tsk As Task
Dim processedCount As Long
Dim skippedCount As Long
Dim errorCount As Long
processedCount = 0
skippedCount = 0
errorCount = 0
On Error GoTo ErrorHandler
‘ —————————————————————————–
‘ 2. トランザクション処理の開始(Undoのグループ化)
‘ —————————————————————————–
App.UndoContext “Task Constraints Clean-up”
App.StartUndo
‘ —————————————————————————–
‘ 3. タスク走査ループ
‘ —————————————————————————–
For Each tsk In prj.Tasks
‘ Nothing判定(削除されたタスクの残骸等への対策)
If Not tsk Is Nothing Then
‘ 【重要】プロジェクトサマリータスク(ID=0)やマイルストーン、外部タスクは除外する
If tsk.ID > 0 And Not tsk.Summary And Not tsk.ExternalTask Then
‘ 例外ルール定義:
‘ 「完了期限(Must Finish On: pjMustFinishOn)」や「指定日終了」などは
‘ ビジネス上の重要な契約マイルストーンである可能性があるため維持する。
‘ ここでは例として「pjAsSoonAsPossible」以外かつ、
‘ 「pjMustFinishOn」「pjMustStartOn」以外の制約をASAPに強制変更する。
Select Case tsk.ConstraintType
Case pjAsSoonAsPossible
‘ すでにASAPなのでスキップ
skippedCount = skippedCount + 1
Case pjMustFinishOn, pjMustStartOn
‘ 厳格な固定日制約は保持する(ログやデバッグ用に出力しても良い)
skippedCount = skippedCount + 1
Case Else
‘ 悪影響を及ぼしやすい制約(SNET, SNLT, FNET, FNLT, ALAP)をASAPにリセット
On Error Resume Next
tsk.ConstraintType = pjAsSoonAsPossible
If Err.Number = 0 Then
processedCount = processedCount + 1
Else
errorCount = errorCount + 1
Err.Clear
End If
On Error GoTo ErrorHandler
End Select
Else
skippedCount = skippedCount + 1
End If
End If
Next tsk
‘ —————————————————————————–
| 4. コミットと復元
‘ —————————————————————————–
App.EndUndo
‘ 計算モードを元に戻す(ここで初めてプロジェクト全体の再計算が走る)
Application.Calculation = originalCalcMode
‘ 処理時間の計測
Dim elapsedTime As Double
elapsedTime = Timer – startTime
‘ 完了レポート
MsgBox “制約条件のクリーンアップが完了しました。” & vbCrLf & _
“———————————-” & vbCrLf & _
“変更されたタスク数: ” & processedCount & vbCrLf & _
“スキップ/維持タスク数: ” & skippedCount & vbCrLf & _
“エラー件数: ” & errorCount & vbCrLf & _
“処理時間: ” & Format(elapsedTime, “0.00”) & ” 秒”, _
vbInformation, “処理成功”
Exit Sub
ErrorHandler:
‘ 異常終了時のリカバリ
Application.Calculation = originalCalcMode
App.SurrenderUndo
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error Number: ” & Err.Number & vbCrLf & _
“Description: ” & Err.Description, _
vbCritical, “致命的エラー”
End Sub
—
4. このコードが「現場のプロ」に選ばれる理由
1. `Application.Calculation = pjManual` による爆速化
MS Projectはタスクのプロパティを1つ書き換えるごとに、WBS全体のクリティカルパスやサマリータスクの期間を再計算しようとする。これを手動モード(Manual)に切り替えることで、数千行のタスク処理であっても一瞬(コンマ数秒)でループが完了する。最後に自動計算に戻した瞬間、一括で健全なスケジュールが再構築される仕組みだ。
2. `UndoContext` によるトランザクションの担保
万が一、予期せぬルール変更や意図しない挙動が発生した場合でも、`App.StartUndo` と `App.EndUndo` で囲んでいるため、Ctrl+Z 一発で実行前の状態に完全ロールバックできる。ツールとしての安全性が段違いに高い。
3. サマリータスク(親タスク)の誤爆防止
MS Projectでは、サマリータスクに対して無理やり制約をかけようとするとCOMエラーを引き起こすか、スケジュール構造が完全に破壊される。コード内で `Not tsk.Summary` を厳格にフィルタリングしているため、子タスクの集計ロジックを汚染しない。
—
5. 運用ルールへの組み込みとデータベース連携への布石
このVBAマクロを単なる「個人の便利ツール」で終わらせてはならない。組織のプロジェクトガバナンスを高めるための運用ルールとして昇華させよ。
- 週次バッチ / レビュー前の定例処理
プロジェクト管理者が毎週金曜日のステータス更新後、またはPMOによる品質監査(Health Check)の直前にこのマクロを実行し、「無用な制約が入っていないか」を強制的に監査するトリガーとする。
- Excel / 外部DB(SQL Server等)との連携
もしWBSをExcelからインポートしている場合、インポート直後のルーチンにこのVBAを組み込む。外部から持ち込まれた「汚染されたスケジュールデータ」を、プロジェクトに組み込む前に自動的に「できるだけ早く(ASAP)」の健全な状態に消毒(Sanitizing)するのだ。
WBSのスケジュールが暴走する言い訳は、もうこのコードの前には通用しない。
手動オペレーションの属人性を排除し、コードの力でプロジェクトの背骨をまっすぐ正すことこそが、真の自動化エンジニアの仕事である。
