【実務・中級編】Projectの「リソースプール」ファイルをVBAで効率的に管理する – Project VBA解析バイブル

スポンサーリンク

Project VBAを掌握する極限の知見:リソースプールファイルをVBAで完全に制御する設計と実装

開発現場において、複数のサブプロジェクト(子ファイル)が単一のマスターリソース(リソースプール)を参照するアーキテクチャは、リソース管理の王道である。しかし、この「リソースプール構造」をGUIの手動操作だけで維持しようとすれば、リンク切れ、重複リソースの乱立、そして最悪の場合はファイル破損という泥沼に引きずり込まれる。

特に、数百規模のタスクを持つプロジェクト群を統括するPMOや、全社的なリソース最適化ツールを構築する開発者にとって、VBAによるリソースプールのプログラム制御は避けて通れない必須スキルだ。

今回は、MS Projectのオブジェクトモデルの裏側にある「ファイル間参照のライフサイクル」を解き明かし、実務で絶対に破綻しない、堅牢なリソースプール管理の自動化手法を伝授する。

—

1. なぜ手動でのリソースプール管理は破綻するのか?

多くの現場で見かけるアンチパターンは、サブプロジェクトを開いた状態でリンク先のリソースプールを変更したり、ファイルパスをハードコーディングしたままネットワークドライブの移動を行ったりすることだ。

MS Projectにおいて、リソースプール(ResourcePool)は、各サブプロジェクトのタスクに対して「どのリソースが、どの程度の稼働率(Units)でアサインされているか」のポインタを保持している。
VBAからこれを操作する際、以下の鉄則を破ると一瞬でリンクが破損する。

  • 排他制御の欠如: プールファイルが開かれていない、あるいは読取専用モードでの競合。
  • パスの絶対参照トラップ: 共有サーバーのドライブレター変更(Zドライブ→Yドライブ等)によるリンク切れ。
  • オブジェクトの解放漏れ: Projectオブジェクトの背後にあるCOMコンポーネントがメモリリークを起こし、ファイルロックが解除されない現象。

これらを完全に克服するためには、「ファイルを開く→安全にリンクを結ぶ→整合性を保って保存・閉じる」という一連のライフサイクルをトランザクションとしてコード化する必要がある。

—

2. 堅牢なリソースプール管理エンジンの設計思想

今回提供するプロダクションコードは、以下の要件を満たす設計にしている。

1. 動的パス解決: 相対パスまたはUNCパスを基準とし、環境変化に強いリンク設定。
2. サイレント処理: 不要なダイアログを表示せず、スクリプトの途中で処理がストップするのを防ぐ (`Application.DisplayAlerts = False`)。
3. エラーハンドリング: リンク先が存在しない場合や、すでにリンク済みのケースをスマートに判定。

—

3. 【コピペ即実戦】リソースプール統合管理モジュール

以下のVBAコードは、アクティブなサブプロジェクトに対して、指定したマスターリソースプールファイルを安全にリンク、またはリンクを更新するための実践的なプロシージャである。

標準モジュールに貼り付けてそのまま利用してほしい。

Option Explicit

‘ ==============================================================================
‘ 処理名: 統合リソースプール・セキュアリンカー
‘ 概要 : 指定したサブプロジェクト(アクティブファイル)に対し、
‘ マスターリソースプールファイルへのリンクを安全に確立・更新する。
‘ ==============================================================================
Public Sub SecurelyLinkResourcePool(ByVal poolFilePath As String)

Dim targetProj As Project
Set targetProj = ActiveProject

‘ 1. プールファイルの存在確認
If Not FSO_FileExists(poolFilePath) Then
MsgBox “致命的なエラー: 指定されたリソースプールが存在しません。” & vbCrLf & poolFilePath, _
vbCritical, “リソースプール管理”
Exit Sub
End If

‘ 2. アプリケーションの警告・画面描画を抑制し、処理の高速化と安定性を担保
On Error GoTo ErrorHandler
Application.ScreenUpdating = False
Application.DisplayAlerts = False

‘ 3. すでに同じプールにリンクされているか確認
If IsAlreadyLinked(targetProj, poolFilePath) Then
MsgBox “このプロジェクトはすでに指定されたリソースプールとリンクしています。”, _
vbInformation, “リソースプール管理”
GoTo CleanUp
End If

‘ 4. リソースプールの共有設定を実行
‘ ResourcePool:=1 (rcPoolSharing): プールファイルを共有するモードを指定
targetProj.ResourcePoolPath = poolFilePath

‘ 変更を保存して整合性を確定
targetProj.Save

MsgBox “リソースプールのリンクが正常に完了しました。”, vbInformation, “成功”

CleanUp:
‘ 5. 状態の復元
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Exit Sub

ErrorHandler:
‘ 異常終了時のフォールバック
Dim errDesc As String
errDesc = Err.Description

Application.ScreenUpdating = True
Application.DisplayAlerts = True

MsgBox “リソースプールのリンク処理中に予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“詳細: ” & errDesc, vbCritical, “致命的エラー”

End Sub

‘ ==============================================================================
‘ 内部関数: 指定ファイルが存在するかを判定
‘ ==============================================================================
Private Function FSO_FileExists(ByVal filePath As String) As Boolean
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
FSO_FileExists = fso.FileExists(filePath)
Set fso = Nothing
End Function

‘ ==============================================================================
‘ 内部関数: 既に同一のプールにリンクされているか判定
‘ ==============================================================================
Private Function IsAlreadyLinked(ByRef prj As Project, ByVal poolPath As String) As Boolean
On Error Resume Next
‘ ResourcePoolPathプロパティは大文字小文字やパス表記ゆれがあるため、
‘ ファイル名レベルあるいは完全一致で比較
Dim currentPool As String
currentPool = prj.ResourcePoolPath

If Err.Number <> 0 Then
IsAlreadyLinked = False
Exit Function
}

If UCase(Trim(currentPool)) = UCase(Trim(poolPath)) Then
IsAlreadyLinked = True
Else
IsAlreadyLinked = False
End If
On Error GoTo 0
End Function

—

4. 応用:複数サブプロジェクトの一括プール更新バッチ

現場の運用では、1つのファイルだけでなく、フォルダ内にある全てのサブプロジェクト(`.mpp`)の参照先プールを一斉に書き換えたいという要望が必ず出てくる。

以下のコードは、指定フォルダ内の全サブプロジェクトをサイレントオープンし、リソースプールのパスを強制的に最新化して上書き保存するマスターバッチである。

Public Sub BatchUpdateResourcePools()
Dim folderPath As String
Dim poolPath As String
Dim fso As Object
Dim folder As Object
Dim file As Object

‘ 設定値(実運用ではセルや外部設定ファイルから読み込むことを推奨)
folderPath = “C:\ProjectData\SubProjects\”
poolPath = “C:\ProjectData\MasterPool\MasterResourcePool.mpp”

Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FolderExists(folderPath) Then
MsgBox “対象フォルダが存在しません: ” & folderPath, vbCritical
Exit Sub
End If

Set folder = fso.GetFolder(folderPath)

Application.ScreenUpdating = False
Application.DisplayAlerts = False

Dim successCount As Long
Dim errorCount As Long

For Each file In folder.Files
If LCase(fso.GetExtensionName(file.Name)) = “mpp” Then
‘ リソースプール本体は一括更新の対象外とする
If InStr(LCase(file.Name), “masterpool”) = 0 Then
On Error Resume Next

‘ バックグラウンドでプロジェクトを開く(読み取り専用ではない)
Dim p As Project
Set p = Application.Projects.Open(file.Path, ReadOnly:=False)

If Err.Number = 0 And Not p Is Nothing Then
p.ResourcePoolPath = poolPath
p.Save
p.Close pjDoNotSave
successCount = successCount + 1
Else
errorCount = errorCount + 1
End If
On Error GoTo 0
End If
End If
Next file

Application.ScreenUpdating = True
Application.DisplayAlerts = True

MsgBox “一括更新が完了しました。” & vbCrLf & _
“成功: ” & successCount & ” 件” & vbCrLf & _
“失敗: ” & errorCount & ” 件”, vbInformation, “バッチ処理終了”

Set fso = Nothing
Set folder = Nothing
End Sub

—

5. チーフアーキテクトからの実務アドバイス

1. ネットワークパス(UNC)の徹底
`Z:\Projects\…` のようなドライブレター依存のパスは、担当者のPC環境によって解決できなくなる。必ず `\\fileserver01\share\Projects\…` というUNC形式で `poolFilePath` を渡すこと。これだけでトラブルの8割を防げる。
2. 競合エラー(Error 1100等)のハンドリング
他のユーザーがリソースプールを開いている状態で書き込みを行おうとすると、MS Projectは容容赦なくランタイムエラーを吐く。本番運用では、ファイルをオープンする前にファイルロック状態を検査するロジック、あるいはリトライ機構を組み込むのがプロフェッショナルだ。

リソースプールの自動化は、単なるコードの記述量ではなく、「プロジェクト管理のデータ構造をどう守るか」というアーキテクチャの理解度試される。この知見をあなたの現場に組み込み、手動運用の悪夢からプロジェクトチームを解放してほしい。

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