【テクニカル・上級編】Resource.Groupを活用したリソース階層管理の自動化 – Project VBA解析バイブル

スポンサーリンク

Resource.Groupを活用した階層管理の自動化:MS Projectを真のERPへと昇華させる極限のアーキテクチャ

プロジェクトマネジメントの現場において、Microsoft Projectは単なる「ガントチャート描画ツール」ではない。それは、工数、コスト、そして組織のケーパビリティを司る動的なリレーショナルデータベースである。

多くのPMやエンジニアが、`Resource.Group` プロパティを単なる「メモ書き程度の文字列属性」として扱っている事実に、私は長年警鐘を鳴らしてきた。このフィールドを「組織階層のキー」として定義し、VBAによって動的に集計・制御することで、MS Projectは全社のリソース稼働率をリアルタイムに可視化する、極めて強力なERP(経営資源計画)エンジンへと変貌する。

本稿では、数万行におよぶタスクと数百名のリソースが交錯する大規模環境を想定し、パフォーマンスとメモリ管理を極限まで追求した、プロフェッショナルなリソース階層管理の自動化手法を詳説する。

—

1. COMオブジェクトの深淵:パフォーマンスのボトルネックを回避せよ

MS ProjectのVBA(Project VBA)において、最も忌避すべきは「不必要なオブジェクトへの再帰的アクセス」である。
`ActiveProject.Resources` コレクションを愚直にループさせ、その都度セルを読み取るコードは、リソース数が増大した瞬間に致命的な遅延を引き起こす。

我々シニアエンジニアが取るべき手法は、「メモリ上での辞書(Dictionary)展開」と「COMインターフェースの最小化」である。

内部メモリ管理の最適化

Projectオブジェクトの操作は、Excel VBA以上にオーバーヘッドが大きい。以下のコードでは、Windows APIを利用した精密な実行速度計測(ミリ秒単位)を行い、オブジェクトのライフサイクルを厳密に管理する。

—

2. 極限の実装:階層別コスト・稼働率集計エンジン

このスクリプトは、`Resource.Group` に格納された「部署名/チーム名」をキーとし、プロジェクト全体のリソースコストと残存作業時間を動的に集計、指定した外部レポート(またはデバッグウィンドウ)へ出力する。

Option Explicit

‘ Windows API: 精密な実行速度計測用
Private Declare PtrSafe Function GetTickCount Lib “kernel32″ () As Long

”’

”’ Resource.Groupをキーとした組織別のコスト・稼働状況の自動集計
”’

Public Sub AggregateResourcePerformance()
Dim startTime As Long
startTime = GetTickCount()

‘ アプリケーションの挙動を抑制し、パフォーマンスを最大化
Application.ScreenUpdating = False
Application.Calculation = pjManual

Dim pj As Project
Set pj = ActiveProject

‘ 組織階層(Group)を保持するためのDictionary
‘ 参照設定: Microsoft Scripting Runtime が必要
Dim groupData As Object
Set groupData = CreateObject(“Scripting.Dictionary”)

Dim res As Resource
Dim grpName As String
Dim resStats As Variant

On Error GoTo ErrorHandler

‘ — データスキャンフェーズ —
For Each res In pj.Resources
‘ 空白や無効なリソースを除外
If Not res Is Nothing Then
grpName = Trim(res.Group)
If grpName = “” Then grpName = “Undefined_Group”

‘ Dictionaryにデータを格納 (Array: 0=Cost, 1=Work, 2=RemainingWork)
If Not groupData.Exists(grpName) Then
groupData.Add grpName, Array(0#, 0#, 0#)
End If

resStats = groupData(grpName)
resStats(0) = resStats(0) + res.Cost ‘ 累積コスト
resStats(1) = resStats(1) + (res.Work / 60) ‘ 累積総工数(h)
resStats(2) = resStats(2) + (res.RemainingWork / 60) ‘ 累積残工数(h)
groupData(grpName) = resStats
End If
Next res

‘ — レポート出力フェーズ —
Debug.Print “— Resource Group Performance Report —”
Debug.Print “Group Name | Total Cost | Total Work(h) | Utilization(%)”

Dim k As Variant
Dim utilization As Double
For Each k In groupData.Keys
resStats = groupData(k)
‘ 稼働率の簡易計算 (実績/全体) ※ロジックは要件に応じてカスタマイズ
If resStats(1) > 0 Then
utilization = ((resStats(1) – resStats(2)) / resStats(1)) 100
Else
utilization = 0
End If

Debug.Print k & ” | ” & FormatCurrency(resStats(0)) & ” | ” & _
Format(resStats(1), “0.0”) & “h | ” & _
Format(utilization, “0.0”) & “%”
Next k

Debug.Print “——————————————”
Debug.Print “Process Completed in: ” & (GetTickCount() – startTime) & “ms”

CleanUp:
‘ 明示的なオブジェクト解放(メモリリーク防止)
Set groupData = Nothing
Set res = Nothing
Set pj = Nothing

Application.ScreenUpdating = True
Application.Calculation = pjAutomatic
Exit Sub

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

—

3. プロフェッショナルが遵守すべき設計思想

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

VBAにおけるメモリ管理は、現代の言語ほど寛容ではない。特にProjectは大規模なバイナリデータを扱うため、`Set res = Nothing` のような明示的解放を怠ると、プロセスのゴーストが残り、ファイルロックや予期せぬクラッシュを招く。

Groupプロパティの「正規化」

`Resource.Group` は自由入力文字列であるため、データクレンジングが不可欠だ。現場では「開発1課」と「開発一課」のような表記揺れが頻発する。本質的な自動化を目指すなら、リソース登録時にWindows APIの `InputBox` やフォームを用いて、あらかじめ定義されたマスターデータから選択させる「入力制御の自動化」も併装すべきである。

レガシー環境への配慮

いまだにProject 2016や2019が混在する環境では、最新の.NET連携(VSTO)よりもVBAの方が可搬性において勝る場合が多い。しかし、VBA内部で `Collection` ではなく `Scripting.Dictionary` を選択するのは、ハッシュテーブルによる $O(1)$ の検索速度を確保するためだ。これは数千のリソースを扱う際の「エンジニアの良心」とも言える選択である。

—

4. システム間連携の極致:CSV/JSONへのストリーム出力

集計したデータをBIツール(Power BIやTableau)へ渡す場合、MS Projectの標準エクスポート機能はあまりに遅く、そして硬直的だ。私は、`ADODB.Stream` または `FileSystemObject` を用いた直接的なストリーム書き出しを推奨する。

‘ FileSystemObjectを用いた高速レポート出力の断片
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
Dim ts As Object
Set ts = fso.CreateTextFile(“C:\Reports\ResourceStats_” & Format(Now, “yyyymmdd”) & “.csv”, True)

ts.WriteLine “GroupName,Cost,Work,Utilization”
‘ … (Dictionaryのループ内で ts.WriteLine を実行) …
ts.Close

このように、Project内部で閉じるのではなく、「データハブとしてのMS Project」を意識したアーキテクチャを構築すること。これこそが、単なるプログラマーと、チーフアーキテクトを分かつ境界線である。

結びに代えて

`Resource.Group` を活用した自動化は、地味に見えてその破壊力は凄まじい。組織のボトルネックを特定し、コストの偏りをあぶり出す。VBAという「古き良き剣」を研ぎ澄ませば、現代の複雑なプロジェクト管理という戦場においても、比類なき武器となる。

諸君も、単なるリファレンスの引き写しではない、ハードウェアとメモリの息吹を感じるコードを書いてほしい。それが、システムを掌握するということだ。

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