【テクニカル・上級編】リソースの「カレンダー例外」をExcelの休日リストから一括適用する高速化処理 – Project VBA解析バイブル

スポンサーリンク

序論:MS Projectという「怪物」を制御する。

Microsoft Project(以下、MSP)は、単なるガントチャート描画ツールではない。その本質は、複雑なクリティカルパス計算エンジンと、リソースの稼働率をミリ秒単位で管理するデータベースの融合体である。

しかし、現場のシニアエンジニアやシステム管理者が直面する最大の壁は、その「脆弱な自動化インターフェース」だ。特に数千名規模のリソースに対し、Excelで管理された特定の休日(例外日)を一括適用しようとすると、標準的なVBAコードでは処理速度が指数関数的に低下し、最悪の場合、スタックオーバーフローやメモリリークでプロセスが沈黙する。

本稿では、レガシーなCOMオートメーションの限界を超え、Windows APIを動員してMS Projectのリソースカレンダー操作を極限まで高速化する手法を詳述する。

—

1. 処理を遅延させる「真の要因」を特定する

なぜカレンダー更新は遅いのか。理由は主に3つある。

1. 再計算の連鎖: 1つの例外日を追加するたびに、MSPは全タスクのスケジュールとリソースの割り当てを再計算しようとする。
2. UIの再描画: 標準の `ScreenUpdating = False` だけでは、内部のグリッド描画ロジックを完全に停止させることはできない。
3. COMのオーバーヘッド: Excelから1セルずつデータを読み込み、MSPのオブジェクトへ渡すという「アプリケーション間通信」が、ループ内で数千回繰り返されることで致命的な遅延を生む。

これらを排除するためのアーキテクチャを構築する。

—

2. 実装:高速化を実現するプロフェッショナル・コード

以下のコードは、単なるマクロではない。リソースのライフサイクルとメモリ効率を考慮した、プロダクション環境に耐えうる「エンジンの設計図」である。

Windows APIによるUI制御と配列処理の融合

Option Explicit

‘ Windows API: MS Projectのウィンドウハンドルを操作し、低レイヤで描画を停止する
If VBA7 Then
Declare PtrSafe Function SendMessage Lib “user32” Alias “SendMessageA” ( _
ByVal hwnd As LongPtr, ByVal wMsg As Long, _
ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
Else
Declare Function SendMessage Lib “user32” Alias “SendMessageA” ( _
ByVal hwnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
End If

Private Const WM_SETREDRAW As Long = &HB

”’

”’ Excelの休日リストを、特定のリソースカレンダーへ高速適用する
”’

Public Sub SyncResourceExceptionsFast()
Dim appMSProject As MSProject.Application
Dim proj As MSProject.Project
Dim res As MSProject.Resource
Dim targetResName As String
Dim holidayData As Variant
Dim i As Long

‘ 1. データソース(Excel)の配列化
‘ セルへのアクセスを最小限にするため、一度にメモリ上の配列へ格納する
holidayData = ThisWorkbook.Sheets(“HolidayList”).Range(“A2:B1000”).Value ‘ A列:名称, B列:日付

Set appMSProject = MSProject.Application
Set proj = appMSProject.ActiveProject

‘ 2. 高速化の儀式:計算モードの停止とUIの物理的フリーズ
appMSProject.Calculation = pjManual
appMSProject.ScreenUpdating = False
SendMessage appMSProject.hwnd, WM_SETREDRAW, 0, 0

On Error GoTo ErrorHandler

‘ ターゲットリソースの特定(ここでは例として全リソースを対象とする)
For Each res In proj.Resources
If Not res Is Nothing Then
‘ リソース固有のカレンダーオブジェクトへの参照
‘ プロパティへのドットアクセスを減らすため、オブジェクト変数に保持
Dim cal As MSProject.Calendar
Set cal = res.StandardCalendar

‘ 3. 例外日の一括登録
‘ 注意: 重複登録はエラーの原因となるため、事前にクリアするか存在チェックが必要
For i = LBound(holidayData) To UBound(holidayData)
If holidayData(i, 2) <> “” Then
‘ MSPのExceptions.Addは、内部でカレンダーの再構築を行う重い処理
‘ 可能な限り「一括」に見えるよう、イベントを抑制した状態で叩く
cal.Exceptions.Add _
Type:=pjDaily, _
Start:=holidayData(i, 2), _
Finish:=holidayData(i, 2), _
Name:=CStr(holidayData(i, 1))
End If
Next i
End If

‘ メモリの明示的解放(ループ内での参照リークを防止)
Set cal = Nothing
Next res

CleanUp:
‘ 4. 復元:UIの再描画許可と再計算の実行
SendMessage appMSProject.hwnd, WM_SETREDRAW, 1, 0
appMSProject.ScreenUpdating = True
appMSProject.Calculation = pjAutomatic
appMSProject.CalculateAll

MsgBox “リソースカレンダーの更新が完了しました。”, vbInformation
Exit Sub

ErrorHandler:
Debug.Print “Error: ” & Err.Description
Resume CleanUp
End Sub

—

3. 極限の知見:アーキテクトが教える最適化のツボ

3.1 `WM_SETREDRAW` の魔力

MS Projectの `Application.ScreenUpdating = False` は、実は非常に「お行儀が良い」実装になっており、内部的なメッセージキューを完全には止めない。Windows APIの `SendMessage` で `WM_SETREDRAW` を `0` に送りつける手法は、OSレベルで描画命令を無視させる。数千行の更新を行う際、これだけで実行速度が30%〜50%向上するケースがある。

3.2 オブジェクトのライフサイクル管理

VBAのガベージコレクションは非力だ。特に `MSProject.Resource` や `MSProject.Calendar` といった重量級のCOMオブジェクトをループ内で扱う場合、使い終わった瞬間に `Set obj = Nothing` を明示的に行うことは、単なる作法ではなく「完走させるための必須条件」である。これを怠ると、中盤から急激にスワップが発生し、処理が重くなる。

3.3 既存の例外日との競合回避

`Exceptions.Add` は、既に同じ日付に例外が存在すると実行時エラーを吐く。これを愚直に `On Error Resume Next` で流すのは三流の仕事だ。
本質的な高速化を求めるなら、まず対象リソースの既存の例外日を `Dictionary` オブジェクト(`Scripting.Dictionary`)に読み込み、ハッシュ照合によって「追加が必要な日付のみ」を抽出してからループを回すべきである。

—

4. レガシー環境と現代的設計の橋渡し

多くの企業では、いまだに Project 2016 や 2019 が現役で稼働している。これらの環境では、.NETへの移行よりもVBAによる「極限の最適化」の方が、展開コストと保守性の面で優位に立つことが多い。

今回の手法は、Excelをデータハブとし、Projectを計算エンジンとして利用する「疎結合なシステム連携」の最小単位である。ここからさらに発展させるならば、休日リストを社内の共有データベース(SQL Server等)から直接ADODBで取得し、Excelを介さずにProjectへ流し込むことで、I/Oのボトルネックをさらに削ぎ落とすことが可能だ。

結論

リソース管理の自動化において、最も重要なのは「ツールに使われないこと」だ。MS Projectのオブジェクトモデルの癖を理解し、低レイヤのAPIとメモリ管理を適切に組み合わせることで、数時間を要していた手作業は数秒の暗転へと変わる。

このコードを手に、あなたの現場の「怪物」を飼い慣らしてほしい。

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