こんにちは!Project VBAの世界へようこそ。
「マクロの記録」ボタンを押すだけの日常から抜け出し、いよいよ本格的なプロジェクト自動化の扉を開こうとしているあなたへ、最高にエキサイティングで実用的な知見をお届けします。
今回は、実務で誰もが一度は頭を抱える「サブプロジェクト統合時のスケジュール崩壊」を、VBAの力で美しく自動解決するエンジンを作ります。
「マスタープロジェクトにサブプロジェクトを挿入したら、謎の制約条件でスケジュールがガタガタになった……」
そんな現場の悲鳴を、今日で終わりにしましょう。ここをクリアすれば、Project VBAの本質が見えてきますよ。バッチリ導いていきますので、安心してついてきてくださいね!
—
1. なぜ、サブプロジェクトを挿入するとスケジュールが狂うのか?
MS Projectでマスタープロジェクトを作り、複数のサブプロジェクト(子ファイル)をぶら下げたとき、こんな現象に直面したことはありませんか?
- 「ASAP(できる限り早く)」で組んでいたはずの子ファイルが、親の制約に引きずられて勝手に「FNET(指定日以降に終了)」に変わる。
- 外部タスクとのリンク(跨ぎリンク)があるのに、サブプロジェクト側の制約が優先されて「遅延の連鎖」が起きる。
Projectのスケジュールエンジンは非常に真面目です。しかし、マスターとサブという「二重の統治構造」が生まれると、制約条件(Constraint)がぶつかり合い、エンジンがパニックを起こして予期せぬ日付を叩き出すのです。
これを人間の手で一つひとつ「制約なし(ASAP)」に直していくのは……想像しただけで発狂しそうになりますよね? だからこそ、VBAで一括浄化するエンジンが必要なのです。
—
2. 制御のキモ:Project VBAにおける「制約条件」の正体
コードを書く前に、Project VBA特有の「重み」を理解しておきましょう。Excel VBAとは違い、Projectのオブジェクトモデルは「タスクのスケジュール計算エンジンと直接対話する」という特徴を持っています。
タスクの制約条件は、VBAでは `ConstraintType` というプロパティで制御します。
代表的なものをいくつか知っておきましょう。
| 定数名 | 意味 | VBAでの数値 |
| :— | :— | :— |
| `pjAsSoonAsPossible` | できる限り早く(ASAP) | 0 |
| `pjAsLateAsPossible` | できる限り遅く(ALAP) | 1 |
| `pjMustStartOn` | [日付]に開始しなければならない | 2 |
| `pjMustFinishOn` | [日付]に終了しなければならない | 3 |
| `pjStartNoEarlierThan` | [日付]以降に開始 | 4 |
サブプロジェクトを統合した際、余計な「Must(〜しなければならない)」や「Start No Earlier Than(〜以降に開始)」が勝手に付与されがちです。今回はこれらを検知し、「外部とのクリティカルな依存関係がない限り、基本はすべて `pjAsSoonAsPossible` (ASAP) に強制リセットする」という賢いロジックを組みます。
—
3. 実装:制約条件を自動調停するVBAスクリプト
それでは、開発環境(Alt + F11)を開き、標準モジュールに以下のコードを貼り付けてください。
実務でそのままコピペして使えるよう、細かくコメントを仕込んでいます。
‘ ==============================================================================
‘ 処理名 : サブプロジェクト統合時 制約条件自動調停エンジン
‘ 概要 : マスタープロジェクト内の全タスクを走査し、不適切な制約条件を
‘ ASAP (pjAsSoonAsPossible) に自動補正する。ただし、マイルストーンや
‘ 外部依存のある重要タスクは保護する。
‘ ==============================================================================
Sub AutoResolveConstraintConflicts()
Dim tsk As Task
Dim correctedCount As Long
‘ 画面描画と計算を一時停止し、爆速で処理を実行する(Project VBAの鉄則!)
Application.ScreenUpdating False
correctedCount = 0
On Error GoTo ErrorHandler
‘ マスタープロジェクト内のすべてのタスクをループ
For Each tsk In ActiveProject.Tasks
‘ 存在しないタスク(空白行など)はスキップ
If Not tsk Is Nothing Then
‘ 【保護ロジック】
‘ 1. サマリータスク(親タスク)は子タスクから自動計算されるため除外
‘ 2. マイルストーン(期間0日)は独自の日付を持つため除外
‘ 3. 「Must Start On」「Must Finish On」などのハード制約は、業務上の意味がある場合もあるため
‘ 今回は「意図せぬ日付固定(Start No Earlier Than等)」に的を絞って解除します。
If Not tsk.Summary Then
‘ 制約タイプが ASAP (0) 以外で、かつ、特定条件に合致する場合
Select Case tsk.ConstraintType
Case pjStartNoEarlierThan, pjStartNoLaterThan, pjFinishNoEarlierThan, pjFinishNoLaterThan
‘ 【応用】もし先行タスク(Predecessors)が存在するなら、
‘ 制約がなくてもスケジュールは自動計算されるため、ASAPに寄せる
If tsk.Predecessors.Count > 0 Then
tsk.ConstraintType = pjAsSoonAsPossible
correctedCount = correctedCount + 1
‘ デバッグウィンドウに修正履歴を出力
Debug.Print “修正完了: ID ” & tsk.ID & ” / 名称: ” & tsk.Name & ” -> ASAPに変更しました。”
End If
Case Else
‘ その他の意図しない制約も、必要に応じてここでキャッチできます
End Select
End If
End If
Next tsk
‘ 処理終了後、画面描画を復元し、プロジェクトを再計算
Application.ScreenUpdating True
Application.CalculateAll
‘ 完了メッセージ
MsgBox “制約条件の自動調停が完了しました。” & vbCrLf & _
“修正されたタスク数: ” & correctedCount & “件”, vbInformation, “自動化エンジン完了”
Exit Sub
ErrorHandler:
‘ エラーハンドリング:万が一のフリーズを防ぐ
Application.ScreenUpdating True
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
End Sub
—
4. コードのここがポイント!プロフェッショナルの知見
初心者の方からよく「なぜこんな書き方をしているの?」と質問を受けるポイントを、チーフアーキテクトの視点から解説します。
① `Application.ScreenUpdating False` の魔法
Excel VBAと同様に、MS Projectでもマクロ実行中に画面を更新させないことで、処理速度が何倍にも跳ね上がります。タスクが数千件ある巨大なマスタープロジェクトでは必須のテクニックです。
② `Not tsk Is Nothing` という安全弁
Project VBAでは、削除されたタスクの跡地や空白行が「Nothing」としてループに入り込むことがあります。これを無視してプロパティ(`tsk.Name`など)にアクセスすると、容赦なく「オブジェクト変数が設定されていません」というエラーで沈没します。このガード文は絶対に省いてはいけません。
③ スケジュールエンジンの再起動 (`Application.CalculateAll`)
コードの最後で強制的に全体再計算を走らせています。Projectは自動計算モードであっても、VBAから大量のプロパティを書き換えた直後は、計算のキュー(予約)が詰まることがあります。最後に手動で「さあ、再計算しなさい!」と号令をかけることで、画面の表示漏れを防ぐのがプロの技です。
—
5. 陥りやすいエラーと対処法
最後に、このスクリプトやProject VBAを扱う上で現場でよくある失敗談をご紹介します。
- エラー:「読み取り専用プロジェクトのため変更できません」
- 原因: サブプロジェクトが「読み取り専用(他のユーザーが編集中、またはリンク切れ)」の状態で開かれています。
- 対策: マスタープロジェクトを開く際、サブプロジェクトも正しく書き込み権限を持って開かれているか(あるいは完全展開されているか)を確認してください。
- 「実行しても何も変わらない気がする……」
- 原因: 修正対象のタスクに「先行タスク(Predecessors)」が設定されていない可能性があります。スクリプト内の `If tsk.Predecessors.Count > 0 Then` という条件がガードしているためです。現場の運用に合わせて、この条件を外すか調整してください。
—
まとめ
いかがでしたでしょうか?
「サブプロジェクト統合時の制約条件競合を自動解消するVBAスクリプト」という少し硬いテーマでしたが、根底にあるのは「Projectのスケジュールエンジンに無駄なパニックを起こさせず、人間は本来のマネジメント業務に集中する」というエンジニアリングのロマンです。
ここをクリアできれば、あなたのProject VBAのスキルは、単なる「お助けマクロ」から「プロジェクト管理インフラの制御」へと一段階上に進化します。
ぜひ、あなたの現場のマスタープロジェクトで試してみてください。「おっ、一瞬でスケジュールが綺麗になった!」という快感を味わえるはずです。それでは、次回の極限知見もお楽しみに!
