Project VBAの深淵:祝日カレンダー一括更新における「メモリと整合性の極致」
Project VBA(Microsoft ProjectのVBAオブジェクトモデル)を扱う際、多くのエンジニアが陥る罠がある。それは「オブジェクトの階層構造を漫然と辿る」という非効率なイテレーションだ。特に数百名規模のリソースを抱えるエンタープライズ環境において、カレンダーの例外(Exception)をループで更新しようとすれば、Projectのオブジェクトモデルはたちまち肥大化し、メモリリークの温床となる。
今日は、祝日データを外部テーブルから読み込み、全リソースへ「静かに、かつ確実に」適用するためのアーキテクチャを紐解く。
1. なぜ「力技のループ」が死を招くのか
Projectオブジェクトモデルにおいて、`Resource.Calendar.Exceptions.Add` を闇雲に呼ぶのは自殺行為に近い。オブジェクト生成のたびにProjectは内部的なメタデータ更新処理を走らせる。これが数千件に達すると、GUIのフリーズや、最悪の場合プロジェクトファイルの破損を招く。
我々が目指すべきは、「状態のクリーンアップ」と「最小限のオブジェクト参照」だ。
2. 実装の要諦:メモリ管理と最適化の定石
コードを書く前に、以下の鉄則を脳に刻んでほしい。
- 明示的なオブジェクト解放: `Set obj = Nothing` は儀式ではない。COMの参照カウントをデクリメントする物理的な命令だ。
- イベントの抑制: `Application.ScreenUpdating = False` は必須だが、さらに `Application.Calculation = pjManual` を併用し、再計算負荷を排除する。
- 例外の重複排除: 既に登録済みの祝日を再登録することは、インデックスの断片化を招く。必ず事前照合を行う。
3. 実装:高効率カレンダーメンテナンスエンジン
外部ファイル(CSVを想定)から祝日を読み込み、全リソースへ適用するコアモジュールを以下に示す。
Option Explicit
‘ 伝説的なチーフアーキテクトによる、Projectカレンダー最適化エンジン
Public Sub SynchronizeHolidayCalendar()
Dim prj As Project
Dim res As Resource
Dim holidays As Collection
Dim holiday As Variant
‘ 1. 環境の凍結(パフォーマンスの極大化)
Application.ScreenUpdating = False
Application.Calculation = pjManual
Set prj = ActiveProject
Set holidays = LoadHolidaysFromCSV(“C:\Data\Holidays.csv”)
On Error Resume Next ‘ 例外処理の局所化
For Each res In prj.Resources
If Not res Is Nothing Then
If res.Type = pjResourceTypeWork Then
Call UpdateResourceCalendar(res.Calendar, holidays)
End If
End If
Next res
On Error GoTo 0
‘ 2. 状態の復帰とリソース開放
Application.Calculation = pjAutomatic
Application.ScreenUpdating = True
Set holidays = Nothing
MsgBox “カレンダーの同期が完了しました。”, vbInformation
End Sub
Private Sub UpdateResourceCalendar(cal As Calendar, holidays As Collection)
Dim ex As CalendarException
Dim hDate As Date
Dim exists As Boolean
Dim item As Variant
For Each item In holidays
hDate = CDate(item)
exists = False
‘ 既存の例外との重複チェック(全探索を避ける最小のロジック)
For Each ex In cal.Exceptions
If ex.Start = hDate Then
exists = True
Exit For
End If
Next
‘ 未登録の場合のみ追加
If Not exists Then
cal.Exceptions.Add Type:=pjDayTypeWorking, Start:=hDate, Finish:=hDate, Name:=”祝日”
End If
Next item
End Sub
4. チーフアーキテクトの視点:レガシーとの対話
Win32 APIによるパフォーマンスの補完
もしこのツールを大規模バッチで実行するなら、`Windows API`の `Sleep` を挟むことを検討すべきだ。Projectはバックグラウンドスレッドでカレンダーの整合性をチェックしている。UIスレッドと同期が取れなくなった瞬間にProjectはクラッシュする。
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
`cal.Exceptions.Add` の直後に `Sleep 10` を入れるだけで、OS側のリソース解放待ちとProject側の整合性チェックの間に「呼吸」が生まれる。このわずかな隙間こそが、システムを止めないための「伝説のエンジニアの知恵」だ。
5. 最後に:コードは「対話」である
VBAはレガシーと言われるが、Projectの深い階層構造を操作する上では、これほど強力なインターフェースはない。重要なのは、オブジェクトがメモリ上でどう振る舞い、Projectのエンジンがどのタイミングで再計算を要求しているかを見極めることだ。
君たちが書く一行のコードが、現場の何百人もの稼働率を左右する。その重みを忘れないでほしい。何かあれば、またコードの深淵で会おう。
