【テクニカル・上級編】VBAによる制約条件の一括削除と、プロジェクトの完全ASAP化ツール – Project VBA解析バイブル

スポンサーリンク

【Project VBAを掌握する極限の知見】荒ぶるスケジュールを鎮めよ:制約条件の一括削除と完全ASAP化エンジンの実装

レガシーなプロジェクト管理システムにおいて、もっとも恐るべき害悪は「ゾンビ化した制約条件」である。
幾度もの仕様変更、場当たり的なパッチ、そして担当者の気まぐれによって植え付けられた無数の「マイルストーン固定」「終了日制約(ALAP/MSOなど)」。これらがスパゲッティのように絡み合ったプロジェクトファイルは、もはやスケジューリングエンジンにとっての黒体輻射であり、内部で無限ループすれすれの再計算地獄を引き起こしている。

シニアエンジニアよ、耳を澄ませ。
「なぜかスケジュールが引けない」「クリティカルパスが歪む」――その原因は、あなたのロジックの拙さではない。肥大化した制約オブジェクトのゴミ屋敷なのだ。

今回は、Microsoft ProjectのVBAオブジェクトモデルの深淵に潜り込み、メモリ効率を極限まで高めた上で、すべてのタスクを可能な限り早期(ASAP:As Soon As Possible)に強制収束させる「プロジェクト完全ASAP化・制約リセットエンジン」の全貌を授ける。

—

1. Project VBAの暗黒面:オブジェクトのライフサイクルと解放の真実

Excel VBAのノウハウのままでProject VBAに挑む者は、必ずやメモリリークとCOM例外の魔神に足を取られる。
Projectのタスク(`Task`)、リソース(`Resource`)、そして制約(`ConstraintType`)は、すべてCOMの境界線を跨いだ向こう側にある。

特に`Tasks`コレクションの走査において、以下の愚行を犯していないか?

‘ 【アンチパターン】これぞ典型的なパフォーマンス崩壊の図
Dim t As Task
For Each t In ActiveProject.Tasks
If Not t Is Nothing Then
t.ConstraintType = pjConstraintASAP
End If
Next t

このコードは、数千行規模のプロジェクトにおいて、COMラッパーの生成と破棄を毎ループで繰り返し、Garbage Collectorを悲鳴を上げさせる。さらに、Projectの内部エンジン(PJM)は、プロパティが変更されるたびにスケジュール再計算のトリガーを引く。これでは数万回の再計算が走り、Excelが固まるどころかOS全体が応答不能になりかねない。

極限最適化の三原則

1. 画面描画と自動計算の完全封鎖 (`ScreenUpdating` / `Calculation` の制御)
2. オブジェクト変数の厳密なスコープ管理と明示的破棄(`Set obj = Nothing`)
3. トランザクション的処理による一括コミット

—

2. 実装:完全ASAP化・制約条件一括リセットエンジン

以下のコードは、単なるマクロではない。荒ぶるスケジュールを更地にし、すべてのタスクの足並みを「ASAP」へと強制的に揃えるための工業用高精度エンジンである。

Option Explicit

‘ ==============================================================================
‘ 模块名: ModScheduleResetEngine
‘ 用途: プロジェクト全体のタスク制約を完全に解除し、純粋なASAPスケジュールへと再構築する
‘ アーキテクト: 伝説のチーフアーキテクト
‘ ==============================================================================

Public Sub ExecuteTotalASAPReset()
Dim startTime As Double
startTime = Timer

‘ 1. システムリソースの保護と爆速化の呪文
‘ 画面描画、バックグラウンド計算、警告ダイアログをすべてシャットダウンする
On Error GoTo ErrorHandler
Application.ScreenUpdating = False

Dim originalCalcMode As Long
originalCalcMode = Application.Calculation
Application.Calculation = pjManual ‘ 手動計算モードへ移行し、無駄な再計算を防ぐ

Dim tsk As Task
Dim totalCount As Long
Dim processedCount As Long
processedCount = 0

‘ アクティブプロジェクトの存在確認
If ActiveProject Is Nothing Then
MsgBox “有効なプロジェクトが開かれていません。”, vbCritical, “致命的エラー”
GoTo Finally
End If

totalCount = ActiveProject.Tasks.Count

‘ ステータスバーに初期メッセージ
Application.StatusBar = “【ASAPエンジン】制約リセット処理を開始します…”

‘ 2. メモリと速度の限界を引き出すタスクループ
‘ For Eachではなく、インデックスアクセスによりオーバーヘッドを極限まで削ぎ落とす
Dim i As Long
For i = 1 To totalCount
Set tsk = ActiveProject.Tasks(i)

‘ Nothing判定およびサマリータスク(プロジェクト要約やWBSの親)の除外
‘ ※親タスクに直接制約をかけるとスケジューリングが破損するため、子タスクのみを対象とするのが定石
If Not tsk Is Nothing Then
If Not tsk.Summary Then

‘ 【コアロジック】
‘ 1. 制約タイプを「できる限り早く (ASAP)」に強制設定
‘ 2. 制約日時(ConstraintDate)をNA(空値)にクリア
‘ ※制約タイプがASAP/ALAP以外の場合、ConstraintDateが残っていると競合エラーが発生する
On Error Resume Next
tsk.ConstraintType = pjConstraintASAP
tsk.ConstraintDate = “NA”
On Error GoTo ErrorHandler

processedCount = processedCount + 1
End If
End If

‘ 軽度なメモリ解放(COMの参照カウンタを適切にデクリメント)
Set tsk = Nothing

‘ 進捗を100件ごとにステータスバーへフィードバック
If i Mod 100 = 0 Then
Application.StatusBar = “【ASAPエンジン】処理中… ” & i & ” / ” & totalCount & ” タスク完了”
DoEvents ‘ UIのフリーズを防ぎつつ、OSに制御を返す
End If
Next i

‘ 3. エンジンの手動再計算と整合性チェックの実行
Application.StatusBar = “【ASAPエンジン】スケジュールを再計算しています…”
Application.Calculation = originalCalcMode

‘ プロジェクト全体の再計算を強制(Ctrl + F9相当)
CalculateAll

Application.ScreenUpdating = True
Application.StatusBar = “”

MsgBox “完全ASAP化が正常に完了しました。” & vbCrLf & _
“処理対象タスク数: ” & processedCount & ” 件” & vbCrLf & _
“処理時間: ” & Format(Timer – startTime, “0.00”) & ” 秒”, _
vbInformation, “Project VBA 限界突破エンジン”

Exit Sub

ErrorHandler:
‘ 異常系ハンドリング:何があってもUI環境を復元する
Application.ScreenUpdating = True
Application.Calculation = originalCalcMode
Application.StatusBar = “”

MsgBox “致命的なエラーが発生しました。” & vbCrLf & _
“Error No: ” & Err.Number & vbCrLf & _
“Description: ” & Err.Description, _
vbCritical, “エンジン異常終了”

Finally:
‘ 確実なオブジェクトの消去
Set tsk = Nothing
End Sub

—

3. コードの急所:シニアだけが知る「見えない罠」

上記のコードには、何年もの修羅場をくぐり抜けてきた者にしか分からない「3つの防壁」が組み込まれている。

① サマリータスク(親タスク)の厳格な除外

Projectにおいて、親タスク(Summary Task)のスケジュールは子タスクの集計値によって自動的に決定される。ここに誤って `pjConstraintASAP` などの制約を書き込もうものなら、PJM(Project Calculation Engine)は内部矛盾を起こし、タスクの期間が勝手に「0日」に縮小したり、スケジュールがロックされるという怪現象を引き起こす。
`If Not tsk.Summary Then` という条件分岐こそが、プロジェクトの崩壊を防ぐ防波堤である。

② 制約日(ConstraintDate)の “NA” クリア

制約タイプを `pjConstraintASAP` に変更するだけでは不十分だ。過去に `MSO`(マイルストーン必着)や `FNLT`(終了期限以降不可)が設定されていた場合、古い `ConstraintDate`(制約日時)の亡霊がデータベースに残存し続ける。
これを `tsk.ConstraintDate = “NA”` によって完全にパージすることで、真の「純粋ASAP状態」が担保される。

③ インデックスアクセスの高速性

`For Each t In ActiveProject.Tasks` は直感的だが、内部でイテレータを生成するため、巨大なプロジェクトでは数秒のロスを生む。あらかじめ `ActiveProject.Tasks.Count` を取得し、`For i = 1 To totalCount` による直接インデックスアクセスを行うことで、コンパイル言語並みのスループットを引き出している。

—

4. システム間連携と今後の展望:VBAの枠を超えて

このVBAツールは、単体で利用するだけでなく、企業内の基幹システム(SAPやP6など)から出力されたXML/MSPDIファイルを自動インポートした直後の「クレンジング・パイプライン」としても機能する。

外部から吐き出された無秩序なスケジュールデータを、夜間バッチやRPA(UiPath等)経由でこのVBAが組み込まれたマスタープロジェクトに流し込み、一瞬で制約を剥ぎ取って完全ASAP化する。これによって、プロジェクトマネージャーは「制約のデバッグ」という不毛な労働から解放され、本来の「リスク管理」に集中できるようになるのだ。

レガシーであれ、VBAであれ、アーキテクチャの理(ことわり)を理解していれば、システムは寸分たがわず意のままに動く。
荒ぶるスケジュールに悩む現場よ、今こそこのエンジンを導入し、秩序を取り戻せ。

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