【実務・中級編】Project VBAにおけるリソースオブジェクトのメモリ解放と安定化 – Project VBA解析バイブル

スポンサーリンク

Project VBAを掌握する極限の知見:リソース管理の闇と、メモリリークを根絶する堅牢な設計

大規模なプロジェクト管理、あるいは複数ファイルにまたがる進捗の自動集計。MS ProjectとVBAを用いた自動化ツールは、正しく作り込めば強力な武器となる。しかし、開発現場のリーダーである私のもとに上がってくるコードの多くは、一つの致命的な爆弾を抱えている。

それが「リソースオブジェクトのメモリリークとCOMコンポーネントの解放漏れ」だ。

「数回まわす分には動くのに、数千件のタスクとリソースを一括処理するバッチを回すと、ExcelやProjectごとフリーズする」
「タスクマネージャーのメモリ使用量が右肩上がりに増え続け、最終的にVBAの実行時エラーが頻発する」

もし、あなたの組んだツールがこのような挙動を示しているなら、原因は明確だ。あなたは「ProjectのCOMオブジェクトのライフサイクル」を支配できていない。

今回は、長時間稼働する自動化ツールにおいて、リソース(Resource)オブジェクトのメモリリークを防ぎ、極限まで安定稼働させるためのプロフェッショナルな設計思想と実装手法を伝授する。

—

なぜ、あなたのVBAコードはメモリリークを起こすのか?

VBAはガベージコレクタ(GC)を持たない言語ではない。厳密に言えば、背後でCOMの参照カウント方式(Reference Counting)によって管理されている。しかし、「`.`(ドット)つなぎでオブジェクトを多用する書き方」や「適切な変数の解放を行わない記述」は、VBAとMS Projectの間に深刻なメモリのデッドロックを生み出す。

1. 暗黙の参照(Hidden Reference)の罠

よく見かける以下のようなコードを見てほしい。

‘ 【アンチパターン】絶対にやってはいけない書き方
ActiveProject.Resources.Add(“リソース名”)
ActiveProject.Resources(1).StandardRate = “5000/h”

一見、何の問題もないように見える。しかし、このコードは背後で複数のCOMオブジェクトを生成し、VBA側から参照をロストしたままメモリ上に残存させる。これを繰り返すと、Projectのプロセス(WINPROJ.EXE)が肥大化し、リソースの登録・割り当て処理中に突如として「オートメーションエラー」を引き起こす原因となる。

2. 変数のスコープと即座の解放

リソースオブジェクト(`Resource`)やリソース割当オブジェクト(`Assignment`)を操作する際は、必ず明示的な変数に受け、処理が終わったら即座に `Nothing` を代入して参照を断ち切る必要がある。これが、プロフェッショナルなProject VBA開発における絶対の鉄則だ。

—

堅牢なリソース登録・割り当てエンジンの設計

実務で耐えうる堅牢なツールを作るためには、以下の3点に配慮した設計が求められる。

1. エラーハンドリングの徹底: 万が一のエラー時でも、COMオブジェクトが宙ぶらりんにならないよう、`Finally` 相当のクリーンアップ処理を構築する。
2. トランザクション的な一括処理: データベースや外部CSVからリソース情報を取得し、Projectへ流し込む際は、画面描画を停止(`ScreenUpdating`)し、オーバーヘッドを最小化する。
3. 重複登録の防止: 既存のリソース名と重複して登録された場合のエラーをハンドリングし、更新(Update)か新規(Add)かを判定するロジックを挟む。

—

【プロダクションコード】メモリリークを根絶するリソース管理モジュール

以下のコードは、外部(例:CSVやExcelのデータテーブル)からリソース情報を読み込み、安全にProjectへ登録・コスト設定を行い、確実にメモリを解放するプロフェッショナル仕様のVBAコードである。

そのままコピー&ペーストし、自身のプロジェクトで活用してほしい。

Option Explicit

‘ =========================================================================
‘ 模块名: clsResourceManager
‘ 概要: MS Project リソース安全登録・コスト設定モジュール
‘ 著作権・設計: 業務自動化アーキテクチャチーム
‘ =========================================================================

Public Sub RegisterResourceSafely(ByVal resourceName As String, ByVal standardRate As Double)
Dim appProj As MSProject.Application
Dim prj As MSProject.Project
Dim targetRes As MSProject.Resource
Dim isRegistered As Boolean
Dim i As Long

‘ エラーハンドリングの有効化
On Error GoTo ErrorHandler

‘ アプリケーションおよびプロジェクトの参照を取得
Set appProj = Application
Set prj = appProj.ActiveProject

‘ 描画停止によるパフォーマンス向上とメモリ負荷軽減
appProj.ScreenUpdating = False

‘ 既存リソースの重複チェック(完全修飾名で走査)
isRegistered = False
For i = prj.Resources.Count To 1 Step -1
‘ 逆順ループにより、コレクション操作時のインデックスズレを防ぐ
Set targetRes = prj.Resources(i)
If Not targetRes Is Nothing Then
If targetRes.Name = resourceName Then
isRegistered = True
Exit For
End If
‘ ループ内でのオブジェクト変数の使い回し時は都度解放
Set targetRes = Nothing
End If
Next i

‘ 登録または更新の分岐
If isRegistered Then
‘ 既存リソースのコスト更新
targetRes.StandardRate = standardRate
Debug.Print “リソース更新成功: ” & resourceName
Else
‘ 新規リソースの追加
Set targetRes = prj.Resources.Add(resourceName)
If Not targetRes Is Nothing Then
targetRes.StandardRate = standardRate
Debug.Print “リソース新規登録成功: ” & resourceName
End If
End If

CleanUp:
‘ =====================================================================
‘ 極めて重要:COMオブジェクトの明示的な解放(メモリリーク防止)
‘ =====================================================================
If Not targetRes Is Nothing Then Set targetRes = Nothing
If Not prj Is Nothing Then Set prj = Nothing
If Not appProj Is Nothing Then Set appProj = Nothing

‘ 画面描画の復元
On Error Resume Next
Application.ScreenUpdating = True
On Error GoTo 0
Exit Sub

ErrorHandler:
‘ 異常系ログ出力とフェイルセーフ
MsgBox “リソース登録中に予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“内容: ” & Err.Description, vbCritical, “Project VBA 致命的エラー”

‘ エラー発生時も確実にメモリリークを防ぐためクリーンアップへジャンプ
Resume CleanUp
End Sub

コードの解説とアーキテクチャのポイント

1. 逆順ループ (`For i = prj.Resources.Count To 1 Step -1`):
コレクションを走査する際、前方から回すと削除や動的変更時にバグを生みやすい。逆順走査は堅牢なVBAコードの基本テクニックである。
2. `targetRes = Nothing` の徹底:
ループの途中や処理の終端で、必ずオブジェクト変数に `Nothing` を代入している。これにより、VBAの背後にあるCOM参照カウンタが確実にデクリメントされ、メモリリークが根絶される。
3. `ScreenUpdating = False` の挟み込み:
リソースの登録・変更ごとにProjectのUIが再描画されるのを防ぐことで、数千件規模のデータを扱う際の処理速度が何倍にも跳ね上がる。

—

データベース・ファイル連携時の実務上の注意点

大規模なプロジェクト管理では、ExcelやSQL Serverなどの外部データソースからリソースや稼働率(Calendar)をインポートすることが多い。ここで陥りがちな罠が「コネクションの維持とファイルロック」だ。

  • ADO/DAOコネクションは必ず閉じて破棄する:

外部DBやExcelファイルを操作した際、`Connection` オブジェクトや `Recordset` オブジェクトの `.Close` と `Set xxx = Nothing` を怠ると、ファイルがロックされたままになり、次回の自動化バッチが起動できなくなる。

  • トランザクションの粒度:

1件ごとにリソースをコミットするのではなく、メモリ上で配列(Array)に展開し、一括してProjectのオブジェクトモデルに流し込む設計(バルク処理)を取り入れること。これが稼働率調整やコスト管理ツールを「実用的な速度」で動かす唯一の解である。

—

結びに代えて

自動化ツールの価値は、「正しく動くこと」だけではない。「何時間、何万回稼働させても、リソースを食いつぶさず安定し続けること」にこそ、エンジニアのプライド宿る。

あなたが構築するProject VBAツールが、一過性のオモチャではなく、現場の信頼を勝ち取る堅牢なインフラストラクチャとなることを願っている。オブジェクトのライフサイクルを支配し、真に洗練された自動化の世界へ踏み出してほしい。

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