巨大なProject VBAを掌握せよ:計算モード制御による起動の極限最適化
プロジェクトマネジメントの世界で、MS ProjectのVBAを扱うことは「巨大な海図を解読する」ことに似ている。数千行のタスク、複雑な依存関係、そして無数のカスタムフィールド。これらを抱えた巨大な `.mpp` ファイルを開く際、VBAエンジニアを絶望させるのは「自動計算」のオーバーヘッドだ。
ファイルを開くたびにProjectエンジンが全タスクの依存関係を再計算し、数分間、画面がフリーズする。これは業務効率の問題ではなく、エンジニアとしての怠慢だ。
今日は、Project VBAのライフサイクルを深く理解し、計算モードを制御することで、この不毛な待機時間を「一瞬」に変える極限の手法を伝授する。
—
なぜ「自動計算」がボトルネックになるのか
Projectの `Calculation` プロパティが `pjAutomatic` に設定されている状態でファイルを開くと、アプリケーションは読み込み完了直後に全スケジュールの再計算を開始する。
巨大なプロジェクトにおいて、この処理はメモリとCPUを著しく浪費する。我々が制御すべきは「開く瞬間の状態」だ。ファイルを開く前に計算モードを強制的に `pjManual`(手動)へ切り替え、必要なデータロードが完了してから計算をキックする。このプロセスを自動化の定石として組み込む必要がある。
—
計算モードを制御する実装コード
以下のコードは、単にファイルを開くだけではなく、アプリケーションの計算状態をバイパスして、起動のオーバーヘッドを最小化する設計となっている。
‘ プロジェクトを開く際の最適化ラッパー
Public Sub OptimizedOpenProject(ByVal filePath As String)
Dim pjApp As Object
Set pjApp = Application
‘ 1. 現在の計算モードを退避(後で戻すため)
Dim originalCalcMode As Long
originalCalcMode = pjApp.Calculation
‘ 2. 計算モードを手動に強制変更し、無駄な再計算を抑止
pjApp.Calculation = pjManual
On Error GoTo Cleanup
‘ 3. ファイルを開く
‘ 読み込み専用かつバックグラウンドでの影響を排除
FileOpenEx Name:=filePath, ReadOnly:=True
‘ — ここで必要なデータ取得や操作を行う —
Debug.Print “データ処理中…”
‘ 4. 処理完了後に計算を実行
pjApp.CalculateProject
Cleanup:
‘ 5. 計算モードを元に戻す(重要:永続的な設定変更を避けるため)
pjApp.Calculation = originalCalcMode
‘ オブジェクトの明示的解放(メモリリーク防止)
Set pjApp = Nothing
End Sub
—
シニアエンジニアが意識すべき「メモリの深淵」
この手法には、さらに踏み込んだ注意点が二つある。
1. アプリケーションスコープの汚染を防ぐ
VBAにおいて、`Application.Calculation` の変更は、そのセッション全体に影響を及ぼす。もし他のアドインや並行して動いているプロセスが「自動計算」を前提としている場合、予期せぬ不整合を引き起こす。必ず `originalCalcMode` を退避し、`Finally` 句に相当する `Cleanup` ラベルで確実に復元すること。
2. Windows APIによる「強制的な解放」
数万行規模のファイルを扱う際、VBAのガベージコレクションだけではメモリが解放されないケースがある。特にCOMオブジェクトを多用する場合、`Set = Nothing` だけでは不十分だ。
極限の環境では、必要に応じて以下のAPIを併用し、メモリの強制フラッシュを検討せよ。
‘ メモリ解放のためのAPI定義(極端なメモリ不足時に使用)
Private Declare PtrSafe Function SetProcessWorkingSetSize Lib “kernel32” ( _
ByVal hProcess As LongPtr, _
ByVal dwMinimumWorkingSetSize As LongPtr, _
ByVal dwMaximumWorkingSetSize As LongPtr) As Long
Private Declare PtrSafe Function GetCurrentProcess Lib “kernel32” () As LongPtr
Public Sub ForceMemoryCleanup()
‘ メモリのワーキングセットを最小化し、OSに返却を促す
SetProcessWorkingSetSize GetCurrentProcess(), -1, -1
End Sub
—
極限の知見:なぜ「自動計算の無効化」が勝つのか
プロジェクトの計算エンジンは「ツリー構造の再帰的な走査」を行う。タスクが1,000を超えると、この走査は指数関数的に複雑化する。起動時に一度だけこれを実行するのと、データのロードと同時に断続的に実行するのでは、後者の方がキャッシュミスが多発し、パフォーマンスが著しく低下する。
「ロード」と「計算」を分離せよ。 これが、巨大なプロジェクトを扱うための唯一の鉄則だ。
最後に:レガシーとの対峙
あなたが保守しているシステムが、10年前の設計だとしても諦める必要はない。計算モードの制御は、Projectのバージョンに依存しない原始的かつ強力なフックポイントだ。APIやメモリ制御を組み合わせ、Projectの「重さ」という概念そのものをコードで支配してほしい。
システムは、書いた通りの速度でしか動かない。そして、その速度を決めるのは常にエンジニアの深い洞察である。
