こんにちは!プロジェクト管理の現場で、日々MS Projectと格闘お疲れ様です。
「マクロの記録」ボタンを押してVBAの世界に足を踏み入れたものの、Excelとは違う独特のオブジェクトモデルに翻弄されていませんか?
今回は、Project VBAの核心に迫る「タスクの階層構造と制約タイプのクレンジング」をテーマに、現場で即戦力となるテクニックを伝授します。
「現場のメンバーが勝手に『指定日開始』なんてガチガチの制約を入れてしまい、スケジュールが身動き取れない……!これを全部『できるだけ早く(ASAP)』に一括変換したい!」
そんな絶望的な状況を、たった数行の洗練されたVBAコードで鮮やかに解決する方法を一緒に見ていきましょう。ここをクリアすれば、あなたもProject VBAの基本はバッチリですよ!
—
なぜ「制約タイプ」のコントロールが重要なのか?
MS Projectには、タスクのスケジュール計算方法を決定する「制約タイプ(Constraint Type)」という概念があります。デフォルトは「できるだけ早く(ASAP: As Soon As Possible)」ですが、ユーザーがうっかり開始日や終了日を入力すると、勝手に「指定日開始(SNET: Start No Earlier Than)」などに変わってしまいます。
これがプロジェクト全体に混入すると、何が起きるでしょうか?
- 前工程が遅れても、勝手に固定された日付でタスクが始まり、全体の辻褄が合わなくなる。
- クリティカルパスの計算が狂い、プロジェクトの本当の遅延リスクが見えなくなる。
つまり、柔軟なWBS(Work Breakdown Structure)を維持するためには、定期的に「余計な制約を剥ぎ取る(ASAPに戻す)」自動化スクリプトが不可欠なのです。
—
Project VBAの基本と「罠」
Excel VBAと違い、Project VBAには特有の「重み」と「お作法」があります。まずは基本のキを押さえましょう。
1. アクティブなプロジェクトを指す `ActiveProject`
Excelの `ActiveSheet` にあたるのが、Projectでは `ActiveProject` です。現在開いているスケジュールファイル全体を指します。
2. タスクコレクションの構造
プロジェクト内のすべてのタスクは `ActiveProject.Tasks` というコレクション(集まり)に格納されています。
ここで注意すべき最初の罠があります。それは、「削除されたタスクやサマリータスク(親タスク)の扱い」です。
VBAでループを回す際、存在しないタスク(Nothing)を誤って触ると、容赦なく実行時エラーが起きて止まってしまいます。プログラミング初学者が最初にハマるポイントです。
—
一括置換エンジンの全体像:実装コード
それでは、プロジェクト全体の全タスクをスキャンし、強制的に「できるだけ早く(ASAP)」に変更する実用コードを公開します。
開発タブのVisual Basic Editor(VBE)を開き、標準モジュールに貼り付けてそのまま実行してください。
Sub ForceResetToASAP()
‘ ===================================================================
‘ 概要: プロジェクト内の全タスクの制約タイプを「できるだけ早く」に一括置換する
‘ アーキテクト特製クレンジングスクリプト
‘ ===================================================================
Dim tsk As Task
Dim targetCount As Long
‘ 処理件数をカウントする変数の初期化
targetCount = 0
‘ 画面描画をロックして処理速度を劇的に向上させる(お作法その1)
Application.ScreenUpdating = False
On Error GoTo ErrorHandler ‘ 思わぬエラーでフリーズするのを防ぐ防壁
‘ プロジェクト内の全タスクをループ処理
For Each tsk In ActiveProject.Tasks
‘ 【重要】タスクが「Nothing(存在しない・空行)」ではないか、
‘ かつ「マイルストーンやサマリータスクではない通常タスクか」等の条件を判定
If Not tsk Is Nothing Then
‘ サマリータスク(親タスク)は制約を変更できない、または変更すべきではない場合があるため除外する例
‘ ※プロジェクトの運用ポリシーに合わせて `If tsk.Summary = False Then` 等で絞り込みます
If Not tsk.Summary Then
‘ 制約タイプが「できるだけ早く (pjAsSoonAsPossible = 0)」以外の場合のみ処理
If tsk.ConstraintType <> pjAsSoonAsPossible Then
‘ 制約タイプを強制的に「できるだけ早く」に変更
tsk.ConstraintType = pjAsSoonAsPossible
‘ 変更したタスクのカウンタをインクリメント
targetCount = targetCount + 1
End If
End If
End If
Next tsk
‘ 画面描画のロックを解除
Application.ScreenUpdating = True
‘ 完了メッセージ
MsgBox “クレンジング完了! ” & targetCount & ” 件のタスクを「できるだけ早く」に変更しました。”, vbInformation, “VBA自動化”
Exit Sub
ErrorHandler:
‘ エラー時のクリーンアップ処理
Application.ScreenUpdating = True
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
End Sub
—
コードの深掘り解説:ここがプロの技
上記のコードには、現場を知るエンジニアならではの「こだわり」が詰まっています。ポイントを解説しましょう。
① `Application.ScreenUpdating = False` による高速化
Projectは、タスクを1行変更するたびにスケジュール全体の再計算(ネットワーク図の再構築)を走らせようとします。タスクが数千件あるWBSでこれをやると、画面がチラつき、完了までに数分かかります。
`ScreenUpdating = False` で描画と計算を裏側に隠すことで、処理をミリ秒単位にまで高速化できます。これは大規模プロジェクトを扱う上での絶対の鉄則です。
② `Not tsk Is Nothing` という安全装置
VBAで `For Each` を回すとき、途中に空行やデータの欠損があると、オブジェクトが `Nothing`(実体なし)になります。これをそのまま `tsk.ConstraintType` などと叩きにいこうものなら、即座に「実行時エラー ’91’: オブジェクト変数または With ブロック変数が見つかりません。」という赤色のダイアログがお出迎えしてくれます。
この防御的プログラミング(Defensive Programming)を挟むだけで、コードの信頼性は劇的に跳ね上がります。
③ 定数 `pjAsSoonAsPossible` の活用
MS ProjectのVBAには、あらかじめ意味のある名前(列挙体:Enum)が用意されています。マジックナンバー(直書きの数字)ではなく `pjAsSoonAsPossible` と書くことで、後から見返したときに「あ、ASAPにしているんだな」と直感的に理解できるようになります。
—
陥りやすいエラーと回避のコツ
1. 「このフィールドは読み取り専用です」と言われる
- 原因: サマリータスク(親タスク)や、すでに完了したタスクの制約を無理やり書き換えようとした場合に発生します。
- 対策: コード内の `If Not tsk.Summary Then` のように、親タスクを処理対象から除外するフィルターを必ずかけましょう。親タスクの日付は、基本的に子タスクの集計値によって自動決定されるべきものです。
2. 変更したのにスケジュールが直らない
- VBAで値を書き換えた後、プロジェクトの計算モードが「手動(Manual)」になっていると、スケジュールが再計算されません。必要に応じて `ActiveProject.CalculationMode = pjAutomatic` などをコードの最初で保証してあげるのもテクニックの一つです。
—
まとめ
今回は、タスクの階層構造を意識しながら「制約タイプ」を一括でクレンジングするVBAスクリプトを解説しました。
- 画面描画の抑制でパフォーマンスを担保する。
- `Nothing` 判定でエラーを未然に防ぐ。
- サマリータスクの除外でプロジェクト構造を破壊から守る。
これらはすべて、実務の荒波にもまれて培われた生きた知見です。マクロの記録の先にある「真のVBA自動化」の扉を、今日、あなたは一つ開きました。
ぜひ明日の業務からこのスクリプトを取り入れ、重たいスケジュール管理から解放されてスマートに成果を出していきましょう!あなたのVBAライフを、これからも応援しています。
