Project VBAを掌握する極限の知見:計算エンジンの「再計算保留」を制し、スケジュール不整合の爆弾を防ぐ法
開発現場でスケジュール計算エンジンを組み込んだVBAツールを運用していて、こんな悪夢を見たことはないだろうか。
「ユーザーが大量のタスクを一括インポートしたが、計算エンジンの『再計算保留(Pending Calculation)』状態を無視して画面を閉じ、結果としてガントチャートのクリティカルパスがズタズタになった。誰もそれに気づかず、翌朝の経営陣向け定例会で大炎上した」
Project VBA(MS ProjectのVBA環境)において、スケジュール計算のライフサイクルを理解していないプログラマは、いわば「時限爆弾を抱えたまま操縦桿を握っている」ようなものだ。MS Projectはパフォーマンス最適化のために、タスクの追加・変更時に即座に再計算を行わず、計算を「保留」状態にすることがある。この状態を検知し、適切なタイミングで強制実行あるいは警告を出さない限り、あなたの組んだロジックは常に破綻のリスクと隣り合わせだ。
今回は、この「再計算保留」の闇を暴き、プロダクション環境で絶対に耐えうる堅牢な監視・制御メカニズムを実装する極限の知見を授けよう。
—
1. なぜ「再計算保留」は見落とされるのか?
MS Projectのオブジェクトモデルにおいて、`Application.Calculation` プロパティやタスク・リソースの操作は、一見すると同期処理のように動く。しかし、複雑な先行・後続関係(FS, SS等)やリソース平準化が絡む大規模プロジェクトでは、パフォーマンス維持のために計算エンジンが遅延評価(Lazy Evaluation)を行う。
愚かな実装者は、データを流し込んだ直後にこう書く。
‘ 【アンチパターン】これでは計算結果の確定を担保できない!
Sub BadAddTasks()
ActiveProject.Tasks.Add Name:=”要件定義”
‘ ここで計算が終わっていない可能性があり、早期リターンするとゴミデータを掴む
MsgBox “タスクを追加しました”
End Sub
このコードの何が問題か。「データ書き込み」と「スケジュール計算の完了」は非同期に近い挙動を示すという点を見落としているのだ。この状態でカスタムフィールドの値や開始日(`Start`)を参照すると、計算前の古い値、あるいは未確定の `NA` を拾うことになり、後続のロジックが完全に狂い始める。
—
2. 堅牢な設計:再計算状態の監視と強制実行のアーキテクチャ
この問題を解決するには、以下の3つのステップを厳格に踏むアーキテクチャが必要となる。
1. 状態の検知: 現在プロジェクトが再計算を必要としているか、あるいは計算中であるかを判定する。
2. 安全装置(ガード): 未計算のままプロセスが終了またはデータ出力されようとした場合、ユーザーに強烈な警告を発する。
3. 強制同期(フラッシュ): 必要に応じてVBA側から計算エンジンを強制的に叩き起こし、計算を完了させる。
MS Projectには、計算モードを制御する `Calculation` プロパティがあるが、手動計算モード(`pjManual`)になっている場合や、自動計算であってもキューがたまっている状態をコードからハンドリングする必要がある。
—
3. 【プロダクションコード】計算エンジン監視・強制実行モジュール
以下のコードは、実務の現場でそのまま組み込める、極限までエラーハンドリングを施したクラス・標準モジュール群の核心部分だ。コピペし、自身のプロジェクトの品質を高めてほしい。
Option Explicit
‘ ==============================================================================
‘ モジュール名: MdlCalculationEngineGuard
‘ 概要: MS Projectの計算エンジン状態を監視・制御し、不整合を防ぐプロ仕様のガード
‘ ==============================================================================
‘ 計算モードの退避用
private m_OriginalCalcMode As Long
”’
”’ または未計算状態を検知して警告を発するメインプロシージャ
”’
Public Sub EnsureCalculationIsComplete(Optional ByVal ForceRecalc As Boolean = True)
On Error GoTo ErrorHandler
‘ 現在の計算モードを記録
m_OriginalCalcMode = Application.Calculation
‘ 1. 手動計算モードになっている場合の危険性を検知
If m_OriginalCalcMode = pjManual Then
Dim userChoice As VbMsgBoxResult
userChoice = MsgBox(“【警告】現在、プロジェクトの計算モードが「手動」に設定されています。” & vbCrLf & _
“このままではスケジュール整合性が保証されません。” & vbCrLf & _
“今すぐ「自動計算」に切り替えて再計算を実行しますか?”, _
vbExclamation + vbYesNo, “計算エンジン警告: 手動モード検知”)
If userChoice = vbYes Then
Application.Calculation = pjAutomatic
Else
Err.Raise vbObjectError + 1000, “EnsureCalculationIsComplete”, “ユーザーにより計算の強制が拒否されました。処理を中断します。”
End If
End If
‘ 2. 計算エンジンの強制実行(Pending状態のフラッシュ)
If ForceRecalc Then
‘ CalculateAllメソッドにより、すべてのオープンプロジェクトの再計算を強制する
Application.CalculateAll
End If
‘ 3. 計算結果の整合性チェック(ダミータスクや主要マイルストーンの検証例)
‘ ※ ここに独自のエラーチェックロジックを挿入可能
If Not ValidateScheduleIntegrity() Then
MsgBox “【重大】スケジュール計算後に矛盾(負ティップスや循環参照等)が検出されました。” & vbCrLf & _
“ガントチャートを確認してください。”, vbCritical, “スケジュール整合性エラー”
End If
Exit Sub
ErrorHandler:
‘ 異常終了時のクリーンアップ
MsgBox “計算エンジン制御中にエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
‘ 必要に応じてログ出力やロールバック処理をここに記述
End Sub
”’
”’ 実務ではここでクリティカルパス上の異常日や制約違反をスキャンする
”’
Private Function ValidateScheduleIntegrity() As Boolean
Dim t As Task
Dim hasError As Boolean
hasError = False
For Each t In ActiveProject.Tasks
If Not t Is Nothing Then
‘ 例: 開始日が終了日を超えている矛盾を検知
If t.Start > t.Finish And Not t.Summary Then
hasError = True
Exit For
End If
End If
Next t
‘ 矛盾がなければTrue、あればFalse
ValidateScheduleIntegrity = Not hasError
End Function
”’
”’
Public Sub BeginBatchOperation()
‘ パフォーマンス最大化のために一時的に画面描画や自動計算を抑制する場合のフック
‘ ※ただし、抑制した場合は必ず最後に EnsureCalculationIsComplete を呼ぶこと
Application.ScreenUpdating = False
‘ 必要であれば手動計算へ切り替え(大規模バッチ時)
‘ Application.Calculation = pjManual
End Sub
”’
”’
Public Sub EndBatchOperation()
‘ 計算モードを自動に戻し、強制計算を実行
Application.Calculation = pjAutomatic
Application.CalculateAll
Application.ScreenUpdating = True
MsgBox “一括処理とスケジュール再計算が正常に完了しました。”, vbInformation, “完了”
End Sub
—
4. データベース連携・外部ファイル出力時の鉄則
この計算エンジン制御が最も重要になるのは、VBAからExcel、Access、あるいはSQL Serverなどの外部DBへデータを吐き出す瞬間だ。
もしあなたが「タスク情報をDBにプッシュするマクロ」を書いているなら、以下の鉄則を絶対に破ってはならない。
> 「データを読み出す(SELECT/Exportする)直前には、必ず `Application.CalculateAll` を挟め」
データベースへ未計算のスケジュール(古い `Start` / `Finish` 日付)を書き込んでしまうと、下流のBIツール(Power BIやTableau)や基幹システムが誤ったデータをベースに稼働し、プロジェクト全体が致命的な誤認に陥る。外部連携モジュールの手前には、必ず先ほど紹介した `EnsureCalculationIsComplete` を門番として配置しなさい。
—
最後に:プログラマの誇りを持て
「動けばいい」という妥協の産物であるコードは、プロジェクトの規模が拡大した瞬間に牙を剥く。特にスケジュール管理という、企業の命運を握るドメインにおいて、計算エンジンのライフサイクルを無視した実装はプロ失格と言わざるを得ない。
今回伝授した「再計算保留の監視と強制同期」の思想をあなたのツールに組み込み、一歩先の、揺るぎない堅牢性を誇る自動化システムを構築してほしい。現場のエンジニアリングをリードするのは、他でもない、あなただ。
