【上級プロ】Projectの「計算エンジン」をVBAで一時停止・再開する際の整合性維持テクニック
Microsoft ProjectのVBA開発において、数千件規模のWBS(Work Breakdown Structure)を一括操作する際、デフォルトの挙動のままコードを実行すればどうなるか。タスクを1行追加・変更するたびにスケジュール計算エンジンが走破し、画面描画と相まって、実行完了までコーヒーを何杯も飲む羽目になる。
このボトルネックを打破するために `Application.Calculation = pjManual` (または `False`)を用いて計算エンジンを一時停止させる手法は、シニアエンジニアの間では常識だ。
しかし、「計算を止めて高速化し、最後に再開する」という単純なアプローチは、依存関係の破壊と致命的なデータの不整合(ゴースト・サマリーやリンク切れ)を引き起こす地雷原である。
今回は、MS Projectのオブジェクトモデルの深層と計算エンジンのライフサイクルを完全掌握し、整合性を1ミリも狂わせずに高速一括処理を完遂するための極限の知見を授ける。
—
1. MS Project計算エンジンの闇:なぜ「止めて動かす」だけでは破綻するのか?
ExcelのVBAであれば、`Application.Calculation = xlCalculationManual` にしても、セル間の参照関係は数式の評価が遅延するだけで、構造自体が壊れることは稀だ。
しかし、Projectの計算エンジンは単なる「数式計算機」ではない。以下を動的に解決する「スケジュール・トポロジー・エンジン」である。
- 先行タスク(FS, SS, FF, SF)に基づくクリティカルパスの再計算
- リソースの過負荷(アサインメントのコンフリクト)の検出
- サマリータスク(WBSの親)のコスト・工数・日程のロールアップ
- 制約条件(「できる限り早く」「指定日以降に着手」など)のバリデーション
計算を手動(Manual)に設定している間、VBAからタスクを追加・削除・リンク(`Task.TaskLinks.Add`)すると、Projectのメモリ上には「数学的に矛盾を孕んだスケジュールツリー」が構築される。この状態で安易に `Calculation = pjAutomatic` に戻す、あるいは不完全な順序でオブジェクトを解放すると、エンジンはパニックを起こし、サマリータスクの日付が異常値(100年後の日付や過去の幽霊タスク)に化ける。
このカオスを防ぐためには、「計算停止中の操作の厳格な順序制御」「依存関係のトポロジカル・ソート」「明示的な再計算トリガーの強制」の3つをコードに組み込まなければならない。
—
2. 実装アーキテクチャ:整合性を担保する一括更新プロシージャ
以下に、数万行規模のエンタープライズWBSであっても、メモリリークを起こさず、整合性を完璧に維持して一括処理を行うための実用コードを示す。
Option Explicit
‘ ==============================================================================
‘ ਾਰ Architectural Note: Project Calculation Engine Controller
‘ ——————————————————————————
‘ 目的: 大規模タスク群のインポート・一括更新における処理速度の極限化と
‘ データ整合性の完全な担保。
‘ 警告: エラーハンドリングを省略したCalculationの切り替えは、プロジェクト
‘ ファイルを破損させるリスクがあります。必ずトランザクション構造を維持すること。
‘ ==============================================================================
Public Sub ExecuteHighSpeedWBSUpdate()
‘ 1. アプリケーション層の最適化退避
Dim origScreenUpdating As Boolean
Dim origCalculation As Long
origScreenUpdating = App.ScreenUpdating
origCalculation = Application.Calculation
‘ トランザクション監視用フラグ
Dim isTransactionStarted As Boolean
isTransactionStarted = False
On Error GoTo ErrorHandler
‘ 2. エンジンの完全静音化と手動モードへの移行
‘ ※ScreenUpdatingをFalseにすることで、UIスレッドへのイベントディスパッチを遮断
App.ScreenUpdating = False
Application.Calculation = pjManual
isTransactionStarted = True
‘ — 【トランザクション開始】 —
Dim prj As Project
Set prj = ActiveProject
‘ オブジェクトのライフサイクル管理用
Dim tskNew As Task
Dim tskPred As Task
Dim i As Long
‘ 模擬的な一括データ登録ループ(数千件を想定)
‘ ※実際にはここでADOやDictionaryからデータをバルクインサートする
For i = 1 to 5000
Set tskNew = prj.Tasks.Add(“タスク_” & i)
tskNew.Duration = “8d”
‘ 依存関係の構築(前タスクが存在する場合)
If i > 1 Then
Set tskPred = prj.Tasks(i – 1)
‘ 計算を手動にしているため、ここでリンクを貼ってもトポロジー計算は走らない
Call tskNew.TaskLinks.Add(tskPred, pjLinkFinishToStart)
End If
‘ 頻繁なメモリ肥大化を防ぐため、オブジェクト変数をこまめに解放
Set tskNew = Nothing
Set tskPred = Nothing
Next i
‘ — 【トランザクション終了前の極意】 —
‘ ここが最大のポイント。計算を自動に戻す「前」に、
‘ Projectの内部タスクインデックスとサマリーの整合性を強制的に整える。
‘ 3. 計算エンジンの段階的復旧と強制再計算
‘ いきなり自動にするのではなく、一度「全体再計算(CalculateAll)」を明示的に叩く
Application.Calculation = pjAutomatic
‘ プロジェクト全体を強制再評価
prj.Calculate
‘ 4. リソース・アサインメントの整合性確認(必要に応じて)
‘ カレンダーやリソースプールとの同期を確実に完了させる
Application.CalculateAll
‘ クリーンアップ
Set prj = Nothing
‘ 状態復元
App.ScreenUpdating = origScreenUpdating
MsgBox “一括更新が整合性を保ったまま正常に完了しました。”, vbInformation, “VBA Architecture”
Exit Sub
ErrorHandler:
‘ 致命的例外発生時のロールバック&安全弁
Dim errDesc As String
errDesc = Err.Description
If isTransactionStarted Then
‘ 異常終了時も必ずエンジンを復旧させないとProject自体がフリーズする
Application.Calculation = pjAutomatic
App.ScreenUpdating = True
End If
‘ オブジェクトの強制解放(メモリリーク防止)
Set tskNew = Nothing
Set tskPred = Nothing
Set prj = Nothing
MsgBox “致命的なエラーが発生しました。整合性を保護するため処理を中断します。” & vbCrLf & _
“詳細: ” & errDesc, vbCritical, “Critical Engine Error”
End Sub
—
3. シニアエンジニアが押さえるべき「3つの極限知見」
① `App.ScreenUpdating` と `Application.Calculation` の二重防壁
画面描画の抑制(`ScreenUpdating = False`)と計算エンジンの停止(`Calculation = pjManual`)はセットでなければならない。
描画スレッドが生き残っている状態で計算エンジンだけを止めると、COMのイベントキューが溢れ、VBAからProjectへの命令がロストするか、最悪の場合メモリーアクセス違反(Access Violation)を引き起こす。
② 例外処理ブロック(`On Error GoTo`)における「二重障害」の回避
VBAで最も恐ろしいのは、エラーハンドラー内でさらなるエラーが発生し、アプリケーションがゾンビ化することだ。
上記のコードでは、`isTransactionStarted` フラグを設け、どの段階で破綻したとしても、必ず `Application.Calculation = pjAutomatic` へ復帰させる安全弁を確実に通す構造にしている。計算手動のままVBAセッションが異常終了すると、ユーザーが手動で開いたプロジェクトファイルすら巻き込んで計算不能状態に陥る。
③ オブジェクトの明示的解放(`Set … = Nothing`)の哲学
VBAのガベージコレクタは優秀に見えて、COMオブジェクト(特にProjectの `Tasks` や `Assignments`)の参照カウントの処理においては信用できない。数千回のループ内でオブジェクト変数を使い回す、あるいは解放しないでおくと、VBAのメモリフットプリントが急増し、OSレベルのスワップが発生してパフォーマンスが台無しになる。
「使ったら即座に `Nothing` を代入する」。この泥臭いメモリ管理こそが、レガシーなVBA環境を何年も安定稼働させる唯一の防壁である。
—
総括
Microsoft ProjectのVBA自動化は、単なるマクロの記録の延長線上には存在しない。それは、背後で稼働する強力かつ繊細な「プロジェクト・スケジュール・エンジン」との高度な対話である。
計算エンジンの停止・再開をマスターすることは、システムの挙動を完全に手中に収めることを意味する。ここで解説したガバナンスとコードパターンをあなたのアーキテクチャに組み込み、圧倒的なパフォーマンスと鉄壁のデータ整合性を手に入れてほしい。
