Project VBAの深淵:リソース割り当ての同期とメモリ管理の極意
Project VBA(Microsoft Project)を大規模環境で運用する際、多くのエンジニアが「リソースシートから割り当て(Assignment)へのデータ同期」で躓く。単なるループ処理で済ませているなら、それはシステムの寿命を縮めているのと同じだ。
今日は、リソースのカスタムフィールド値を全Assignmentへ伝播させる際、なぜメモリリークが発生し、なぜプロセッサを枯渇させるのか。その「極限の知見」を共有する。
—
1. なぜ「単純なループ」が死を招くのか
Projectのオブジェクトモデルは、階層が深くなるほどCOMインターフェースの呼び出しコストが跳ね上がる。`ActiveProject.Assignments`を不用意にループさせると、メモリ上のオブジェクト参照が適切に解放されず、Windowsのヒープメモリを圧迫し続ける。
シニアエンジニアが意識すべきは、「オブジェクトの生存期間(Lifetime)」と「COM参照カウントの制御」だ。VBAはガベージコレクションを備えていない。明示的な参照の切断こそが、大規模プロジェクトファイルを安定させる唯一の防波堤である。
—
2. 実装:パフォーマンスを極限まで高める同期エンジン
以下は、リソースのカスタムフィールド値を、そのリソースに関連する全てのAssignmentへ伝播させるための最適化コードだ。
Option Explicit
‘ メモリ最適化のためのAPI宣言(必要に応じて使用)
If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
End If
Public Sub SyncResourceCustomFields()
Dim proj As Project
Dim res As Resource
Dim asn As Assignment
Dim tsk As Task
Set proj = ActiveProject
‘ 画面更新を停止:描画コストを排除
Application.ScreenUpdating = False
On Error GoTo Cleanup
‘ リソース単位で走査
For Each res In proj.Resources
If Not res Is Nothing Then
‘ ターゲットとなるカスタムフィールド値を取得(Text1を例とする)
Dim targetValue As String
targetValue = res.Text1
‘ リソースが割り当てられている全タスクを走査
For Each asn In res.Assignments
If Not asn Is Nothing Then
‘ 比較して更新(不要な書き込みを避けてイベント発火を最小化)
If asn.Text1 <> targetValue Then
asn.Text1 = targetValue
End If
End If
‘ オブジェクトの参照解除
Set asn = Nothing
Next asn
End If
Set res = Nothing
Next res
Cleanup:
Application.ScreenUpdating = True
‘ 参照の完全解放
Set proj = Nothing
If Err.Number <> 0 Then MsgBox “Error: ” & Err.Description
End Sub
—
3. シニアエンジニアが守るべき3つのアーキテクチャ原則
① イベント抑制と更新の最小化
`asn.Text1 = targetValue` の行に注目してほしい。無条件で代入するのではなく、必ず比較を行っている。Projectのカスタムフィールドは変更されるたびに計算エンジンをキックする可能性がある。変更がない場合は書き込まない、これが大規模スケジュールにおける鉄則だ。
② 明示的なメモリ解放の徹底
VBAの `For Each` ループ内でオブジェクト型変数を使い回すのは危険だ。上記のコードのように、ループの最後で `Set asn = Nothing` を明示的に呼び出すことで、参照カウントを確実にデクリメントさせる。これにより、数万行規模のスケジュールでもメモリのフラグメンテーションを防げる。
③ COMのオーバーヘッドを避ける
もしプロジェクトの規模が1万Assignmentを超えるなら、オブジェクトモデルを直接叩くのではなく、一度 `Filter` をかけて対象を絞り込むか、あるいは `Project.Application.Calculation = pjManual` に変更してから処理を行う検討をすべきだ。
—
結びに:レガシーとの付き合い方
Project VBAは古い技術だが、APIの深い部分を知れば、現代の軽量なフレームワークよりも遥かに強力な一括操作を可能にする。
「動けばいい」というコードは、数ヶ月後のシステム保守を地獄に変える。リソース管理において、データの一貫性はシステムの信頼性そのものだ。今日紹介したメモリ制御と更新ロジックを、あなたのコードベースに組み込んでみてほしい。
もし、さらに深い「Windows APIを通じたProjectの非同期制御」や「外部データベースとのバイナリ同期」について知りたいなら、また次の機会に話そう。
現場からは以上だ。コードを慈しめ。システムは必ず応えてくれる。
