【テクニカル・上級編】【中級者向け】複数プロジェクトの同時オープンとメモリ解放のベストプラクティス – Project VBA解析バイブル

スポンサーリンク

墓場まで持っていく「Project VBA」のメモリ制御術:複数同時処理の極意

多くの開発者が、Project VBAにおける「プロジェクトの連続処理」で躓く。`Application.Projects.Open`を繰り返し、気がつけばメモリ使用量は肥大化し、最後は`Automation Error`や`不可解なクラッシュ`で幕を閉じる。

なぜか? 多くの者は`Project`オブジェクトを「単なる変数」としてしか見ていないからだ。VBAのガベージコレクションは、COMオブジェクトの参照カウントが0になるまで機能しない。そして、Projectの内部エンジンは非常に重い。

今日は、中級者の壁を越え、システムの生存率を極限まで高めるための「真のメモリ管理」について語る。

1. 参照の「断ち切り」は宣言とセットで行え

VBAにおいて`Set prj = Nothing`を呼び出すだけでは不十分なケースが多い。特にプロジェクト間の依存関係が複雑な場合、Project内部のキャッシュが解放されないことが往々にしてある。

以下のコードは、私が現場で必ず実装する「オブジェクトの完全なる破棄」のテンプレートだ。

Public Sub ProcessProjectSafely(ByVal filePath As String)
Dim prj As MSProject.Project

‘ 1. プロジェクトを開く(Read-Onlyで開くのが基本)
Set prj = Application.Projects.Open(filePath, ReadOnly:=True)

On Error GoTo Cleanup

‘ — ここにロジックを記述 —
Debug.Print “Processing: ” & prj.Name
‘ —————————

Cleanup:
‘ 2. エラー発生時でも必ずクローズし、参照を解放する
If Not prj Is Nothing Then
‘ 保存なしで閉じる(読み取り専用ならこれで十分)
prj.Close pjDoNotSave

‘ 参照カウントを強制的に叩き落とす
Set prj = Nothing
End If

‘ 3. GCを待たずにメモリを解放したい場合のTips
‘ DoEventsを挟むことで、バックグラウンドのCOM処理を完了させる
DoEvents
End Sub

2. 隠れた「ゾンビ・プロセス」を許すな

`Application.Projects.Open`を繰り返すと、見えないバックグラウンドでCOMコンポーネントが残存する。これを防ぐには、`MSProject.Application`を外部から制御する「アウトプロセス」での設計を推奨する。

社内システム管理者が大規模なバッチ処理を組む場合、ExcelからProjectを制御する構成が多いだろう。その際、必ず以下のAPIでプロセスを明示的に終了させる必要がある。

‘ プロセス終了のためのWindows API定義
Private Declare PtrSafe Function TerminateProcess Lib “kernel32” (ByVal hProcess As LongPtr, ByVal uExitCode As Long) As Long

‘ 現場で多用する終了シーケンス
Public Sub ForceKillProject(ByRef projApp As MSProject.Application)
If Not projApp Is Nothing Then
‘ まずは正攻法で閉じる
projApp.Quit
Set projApp = Nothing

‘ 極限環境ではここでDoEventsをループさせ、メモリの解放を待機する
‘ あるいは、タスクマネージャーから「WinProj.exe」を強制終了する
‘ APIを呼ぶのは最終手段だが、レガシー環境でのメモリリーク対策には最強の武器となる
End If
End Sub

3. なぜ「ベースライン」でメモリが爆発するのか

ベースラインの保存や設定は、Projectの内部テーブル(`TaskBaseline`等)を激しく書き換える。これをループ処理で行う際、メモリが枯渇する最大の理由は「再計算」だ。

解決策: 処理の開始前に`CalculationManual`へ切り替えること。これだけでメモリ使用量と処理速度は劇的に改善される。

‘ 大規模処理の定石:再計算を止めてから実行する
Application.Calculation = pjManual

‘ — 複雑なベースライン更新処理 —
‘ …

‘ 最後に再計算を戻す
Application.Calculation = pjAutomatic
Application.CalculateAll

4. チーフアーキテクトからの忠告

君たちが書いているコードは、単に「動く」だけではいけない。何百ものプロジェクトファイルを処理し、その後にProjectアプリを落とした際、タスクマネージャー上のメモリ使用量が綺麗に初期値まで戻るか? それを検証できて初めて「エンジニア」だ。

  • 循環参照を避ける: `Project`オブジェクトをグローバル変数に保持するな。
  • イベントの無効化: `Application.EnableEvents = False`を忘れずに。不要なイベント発火がメモリを食いつぶす。
  • インデックスでアクセスしない: ループ処理では`For Each`を使い、メモリ上のオブジェクトアドレスを直接操作する感覚を持て。

プロジェクトファイルの管理は、結局のところ「リソースの管理」そのものだ。メモリという有限のリソースを制する者が、Project VBAという広大な戦場を支配する。

次は、プロジェクト間のデータ同期における「トランザクションの完全性」について語ろうか。準備ができたら、またここへ来るといい。

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