MS Project VBAを極限まで支配する:リソース矛盾・過負荷を秒速で炙り出す「監査エンジン」の構築
現場で動く大規模なMicrosoft Project(MSP)のスケジュールを眺めていて、絶望したことはないか?
「スキルセットと全く噛み合わないリソースがアサインされている」「特定のシニアエンジニアにだけ120%の負荷が集中している」「存在しないオーバーアロケーション(過剰割り当て)が隠蔽されている」。
UIからこれを人間が目視でチェックするなど、10万行を超えるレガシーコードのデバッグと同等の苦行である。
シニアアーキテクトであれば、こうした人海戦術の監査は即座にコードで自動化すべきだ。
今回は、Project VBAのオブジェクトモデルの挙動、メモリ管理の罠、そして実務で耐えうる堅牢なバリデーションチェックツールの実装コードを公開する。
—
1. Project VBAにおける「見えない罠」とメモリ最適化の極意
VBAはガベージコレクションを持たない。特にCOMオブジェクトの塊であるMS Projectを操作する場合、インスタンスの解放を怠れば、メモリリークだけでなく、最悪の場合はMSPプロセス自体が沈黙する(いわゆるゾンビプロセスの発生)。
オブジェクトの明示的解放と`Nothing`代入の鉄則
ループ内で `Task` や `Resource` オブジェクトを繰り返し取得・生成する際、ローカル変数のスコープ管理を誤るとメモリが圧迫される。
以下の鉄則を遵守しろ。
- `Set obj = Nothing` をループの末尾で必ず実行する。
- Variant型の暗黙的バインドを排除し、厳密な型定義(`As Task`, `As Resource`)を行う。
- コレクションのカウントはループの都度評価させず、最初に変数にキャッシュする(パフォーマンス最適化)。
—
2. リソース監査エンジンの設計思想
今回構築するツールは、以下の2つの軸でタスクとリソースの整合性を監査する。
1. スキルミスマッチ検知: タスクのテキストフィールド(例: `Text1`を要件定義用スキルタグとする)と、割り当てられたリソースのグループ(`Resource.Group`)の不一致。
2. 過剰割り当て(オーバーアロケーション)の深層検知: 標準的な超過稼働だけでなく、タスクの期間とリソースの最大単位(`PeakUnits`)を監視し、隠れたボトルネックを抽出。
検出結果は、別シート(またはCSV/ログ出力)ではなく、MSP自体のタスクビューを汚さずに、専用のレポート用Excelワークブックへ動的出力する設計とする。
—
3. 実装コード:リソース矛盾・過負荷自動検知マクロ
以下のコードをProject側のVBAエディタ(標準モジュール)に配置し、実行せよ。
Option Explicit
‘ ==============================================================================
‘ 業務自動化アーキテクチャチーム
‘ módulo: 統合リソース監査エンジン (Resource Audit Engine for MS Project)
‘ ==============================================================================
Public Sub ExecuteResourceAudit()
Dim prj As Project
Set prj = ActiveProject
Dim tsk As Task
Dim asm As Assignment
Dim res As Resource
Dim auditCount As Long
auditCount = 0
‘ Excel出力用の準備(COM連携によるレポート生成)
Dim xlApp As Object
Dim xlWb As Object
Dim xlWs As Object
On Error Resume Next
Set xlApp = CreateObject(“Excel.Application”)
On Error GoTo 0
If xlApp Is Nothing Then
MsgBox “Excelのインスタンス生成に失敗しました。環境を確認してください。”, vbCritical, “致命的エラー”
Exit Sub
End If
xlApp.Visible = True
Set xlWb = xlApp.Workbooks.Add
Set xlWs = xlWb.Sheets(1)
xlWs.Name = “Resource_Audit_Report”
‘ ヘッダーの設定
xlWs.Cells(1, 1).Value = “タスクID”
xlWs.Cells(1, 2).Value = “タスク名”
xlWs.Cells(1, 3).Value = “要求スキル (Text1)”
xlWs.Cells(1, 4).Value = “割当リソース名”
xlWs.Cells(1, 5).Value = “リソースグループ”
xlWs.Cells(1, 6).Value = “ピーク稼働率 (%)”
xlWs.Cells(1, 7).Value = “検知ステータス”
Dim rowIdx As Long
rowIdx = 2
‘ メモリ効率化のため、エラーハンドリングを包含した走査
On Error GoTo ErrorHandler
Dim tskCount As Long
tskCount = prj.Tasks.Count
Dim i As Long
For i = 1 To tskCount
Set tsk = prj.Tasks(i)
‘ サマリータスクや削除済みタスクはスキップ
If Not tsk Is Nothing Then
If Not tsk.Summary And tsk.Active Then
Dim requiredSkill As String
requiredSkill = Trim(CStr(tsk.Text1)) ‘ Text1をスキル定義領域として利用
‘ 割り当てられたリソースを走査
For Each asm In tsk.Assignments
Set res = asm.Resource
If Not res Is Nothing Then
Dim isMismatch As Boolean
isMismatch = False
Dim isOverAllocated As Boolean
isOverAllocated = False
‘ 1. スキルミスマッチの判定ロジック
‘ (例: タスクのText1とリソースのGroupが完全一致しない場合を検知)
If requiredSkill <> “” Then
If InStr(1, res.Group, requiredSkill, vbTextCompare) = 0 Then
isMismatch = True
End If
End If
‘ 2. 過剰割り当て(PeakUnitsが100%超)の判定
If asm.PeakUnits > 1 Then ‘ Project VBAでは100% = 1.0
isOverAllocated = True
End If
‘ 異常検知時にレポート行を出力
If isMismatch Or isOverAllocated Then
auditCount = auditCount + 1
xlWs.Cells(rowIdx, 1).Value = tsk.ID
xlWs.Cells(rowIdx, 2).Value = tsk.Name
xlWs.Cells(rowIdx, 3).Value = IIf(requiredSkill = “”, “未定義”, requiredSkill)
xlWs.Cells(rowIdx, 4).Value = res.Name
xlWs.Cells(rowIdx, 5).Value = res.Group
xlWs.Cells(rowIdx, 6).Value = asm.PeakUnits 100
Dim statusMsg As String
statusMsg = “”
If isMismatch Then statusMsg = statusMsg & “[スキルミスマッチ] ”
If isOverAllocated Then statusMsg = statusMsg & “[過剰割り当て(>100%)]”
xlWs.Cells(rowIdx, 7).Value = statusMsg
rowIdx = rowIdx + 1
End If
Set res = Nothing
End If
Set asm = Nothing
Next asm
End If
Set tsk = Nothing
End If
Next i
‘ フォーマット調整
xlWs.Columns(“A:G”).AutoFit
MsgBox “監査完了。検知件数: ” & auditCount & ” 件”, vbInformation, “監査エンジン正常終了”
CleanUp:
‘ 厳格なオブジェクト解放(メモリリーク防止)
Set xlWs = Nothing
Set xlWb = Nothing
Set xlApp = Nothing
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “実行時エラー”
Resume CleanUp
End Sub
—
4. コードの深層解説:プロフェッショナルが実装すべき理由
1. `ActiveProject.Tasks` のインデックスアクセス
`For Each` よりも `For i = 1 To prj.Tasks.Count` を用いることで、大規模プロジェクトにおけるメモリフラグメンテーションを回避し、CPUキャッシュヒット率を高めている。
2. `Not tsk Is Nothing` によるNULL防御
MSPではタスクの削除や挿入により、インデックスの欠損や空のオブジェクトが返ることが多々ある。これを弾く防壁がシステムクラッシュを防ぐ。
3. 完全なCOM解放チェーン
`Set res = Nothing`, `Set asm = Nothing`, `Set tsk = Nothing`, そして最終的なExcelオブジェクト群の破棄。これにより、VBA実行後にExcelプロセスがタスクマネージャーに幽霊のように残留する現象(ゾンビプロセス)を完全に根絶している。
—
5. まとめとアーキテクトからの提言
リソースの割り当てミスや過負荷は、プロジェクトの遅延という「結果」として現れる前に、コードによって「予兆」の段階で叩き潰さなければならない。
今回提供した監査エンジンは、単なるバリデーションツールではない。組織のPMO(プロジェクトマネジメントオフィス)におけるガバナンスを自動化するための「強力な武器」である。これを定期バッチ、あるいはプロジェクトのベースライン変更時に自動フックさせることで、破綻する前にプロジェクトを救うことが可能となる。
泥臭い手作業のスケジュール管理は、今日で終わりにしよう。コードでプロジェクトを支配せよ。
