【テクニカル・上級編】【上級者向け】プロジェクトのグローバルテンプレート(.MPT)を動的に切り替える運用 – Project VBA解析バイブル

スポンサーリンク

Project VBAを掌握する極限の知見:グローバルテンプレート(GLOBAL.MPT)の動的スイッチングアーキテクチャ

Microsoft Projectの裏側で、すべてのカスタムフィールド、テーブル、フィルター、ビューの運命を握る幽霊、それが `GLOBAL.MPT` だ。
複数のプロジェクトマネージャーが混在する環境、あるいは全社共通のPMO標準と特定巨大案件向けの特異なカスタム定義が衝突する現場において、デフォルトのグローバルテンプレートに依存することは、システム的な爆弾を抱えて自社ビルから飛び降りるようなものである。

今回は、Project VBAとWindows環境の深層を突き詰め、実行時に動的に `GLOBAL.MPT` を切り替えることで、環境依存のトラブルを根絶し、完全なエンタープライズ・ガバナンスを実現するための極限の知見を授けよう。

1. 根本課題:なぜデフォルトの `GLOBAL.MPT` 運用は破綻するのか

Microsoft Projectは起動時、ユーザープロファイルのローカル領域に存在する `GLOBAL.MPT` を排他的にロードする。
このアーキテクチャには、シニアエンジニアが直面せざるを得ない致命的な構造的欠陥がある。

  • ローカルキャッシュの汚染: ユーザーがアドホックに作成したビューやローカルマクロがグローバル側に書き戻され、別案件を開いた瞬間に名前空間が衝突する。
  • 環境間のバージョン乖離: 配布すべき企業標準(PMOテンプレート)が更新された際、各クライアント端末のローカル `GLOBAL.MPT` を手動、あるいは不完全なスクリプトで上書き同期させることで発生する破損(Corruption)。
  • ファイルロックの競合: 複数インスタンスやバックグラウンド連携時にMAPI/MPTファイルがロックされ、`Error 1100` や予期せぬサイレントクラッシュを引き起こす。

これを解決する唯一の手段は、「どのプロジェクトファイルをどの文脈で開くか」をVBAのライフサイクル初期にフックし、適切なマスターMPTを動的に指し示してプロセスを起動・制御する仕組みの構築である。

2. アーキテクチャの全体像:動的スイッチングのメカニズム

MS Project自体には、コマンドラインスイッチとして `/m` や直接的なMPT指定の柔軟性が欠けている場合がある(特に古いバージョンやレガシーなOfficeスイート統合環境において)。
そのため、真に堅牢なシステムを構築するには、以下の2段階のアプローチを採用する。

1. VBAランタイム制御によるパスの差し替え: Project本体の起動後、アプリケーションレベルのイベントまたは外部VBAブートストラップから、対象のMPTの参照先を動的に切り替えてメモリ上にロードし直す。
2. Windows API / ファイルI/Oによる排他制御と整合性検証: 読み込ませるMPTが別プロセスに掴まれていないか、破損していないかをコードレベルで担保する。

3. 実装コード:動的MPTスイッチング・エンジン

以下のコードは、単なる「ファイルを開くマクロ」ではない。環境依存性を完全に排除し、指定されたプロジェクト固有のMPTコンテキストを強制適用するためのチーフアーキテクト級の実装だ。

このコードは、Projectの標準グローバルテンプレート上ではなく、独立したブートストラップ用アドイン(`.add` または中央集権的なマスター `.mpp`)に配置することを想定している。

Option Explicit

‘ Win32 API declarations for robust file existence and locking checks
If VBA7 Then
Private Declare PtrSafe Functionüsü Lib “kernel32” Alias “SetDllDirectoryW” (ByVal lpPathName As LongPtr) As Long
Private Declare PtrSafe Function CreateFileW Lib “kernel32” ( _
ByVal lpFileName As LongPtr, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, _
ByVal lpSecurityAttributes As LongPtr, ByVal dwCreationDisposition As Long, _
ByVal dwFlagsAndAttributes As LongPtr, ByVal hTemplateFile As LongPtr) As LongPtr
Private Declare PtrSafe Function CloseHandle Lib “kernel32” (ByVal hObject As LongPtr) As Long
Else
‘ 32bit legacy support if necessary (though modern enterprise mandates 64bit)
Private Declare Function CreateFileW Lib “kernel32” ( _
ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, _
ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, _
ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib “kernel32” (ByVal hObject As Long) As Long
End If

Const GENERIC_READ = &H80000000
Const FILE_SHARE_READ = &H00000001
Const FILE_SHARE_WRITE = &H00000002
Const OPEN_EXISTING = 3
Const INVALID_HANDLE_VALUE = -1

/

  • 指定されたプロジェクトファイルと、それに紐づく専用MPTを安全にロードする
  • @param targetMppPath 読み込む対象のプロジェクトファイルパス
  • @param targetMptPath 適用すべき専用グローバルテンプレート(.mpt)のパス

/
Public Sub OpenProjectWithCustomGlobal(ByVal targetMppPath As String, ByVal targetMptPath As String)

Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ 1. プリフライト検証:ファイルの物理存在確認と排他ロックチェック
If Not fso.FileExists(targetMppPath) Then
MsgBox “致命的なエラー: 対象プロジェクトファイルが存在しません。” & vbCrLf & targetMppPath, vbCritical, “Enterprise PMO Gateway”
GoTo CleanUp
End If

If Not fso.FileExists(targetMptPath) Then
MsgBox “致命的なエラー: 指定されたカスタムMPTが存在しません。” & vbCrLf & targetMptPath, vbCritical, “Enterprise PMO Gateway”
GoTo CleanUp
End If

If IsFileLocked(targetMptPath) Then
MsgBox “警告: 指定されたMPTファイルは現在別のプロセスによってロックされています。”, vbExclamation, “Enterprise PMO Gateway”
GoTo CleanUp
End If

‘ 2. MS Projectのグローバルテンプレートパスを一時的にオーバーライド
‘ 注意: Applicationオブジェクトのプロパティ経由で直接GLOBAL.MPTを完全動的ロードするのは
‘ Projectのアーキテクチャ上制限があるため、Organizerインポートによる動的同期を併用する。

On Error GoTo ErrorHandler

‘ 対象プロジェクトを通常通り開く
Dim prj As Project
Set prj = Application.Projects.Open(targetMppPath)

‘ 3. カスタムMPTから最新の定義(ビュー・テーブル・フィルター等)をメモリ上のアクティブプロジェクトに動的インポート
Call SyncDefinitionsFromCustomMPT(targetMptPath, prj)

MsgBox “プロジェクトのロードおよびカスタムMPTの適用が正常に完了しました。”, vbInformation, “Enterprise PMO Gateway”

CleanUp:
Set fso = Nothing
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “Runtime Exception”
Resume CleanUp
End Sub

/

  • Windows APIを使用してファイルが他プロセスに占有されているかを厳密に判定

/
Private Function IsFileLocked(ByVal filePath As String) As Boolean
Dim hFile As LongPtr
#If VBA7 Then
hFile = CreateFileW(StrPtr(filePath), GENERIC_READ, FILE_SHARE_READ Or FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0)
#Else
hFile = CreateFileW(filePath, GENERIC_READ, FILE_SHARE_READ Or FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0)
#End If

If hFile = INVALID_HANDLE_VALUE Then
IsFileLocked = True
Else
IsFileLocked = False
CloseHandle hFile
End If
End Function

/

  • Organizerオブジェクトを駆使し、外部MPTの定義をアクティブプロジェクトに動的マージする

/
Private Sub SyncDefinitionsFromCustomMPT(ByVal mptPath As String, ByRef targetPrj As Project)
‘ MS ProjectのOrganizer機能を利用し、指定MPTから必要なビューやカスタムフィールドの定義を流し込む
‘ これにより、ローカルの汚染されたGLOBAL.MPTに依存しないクリーンな環境を強制する

On Error Resume Next

‘ 例:カスタムビューの同期(必要に応じてTables, Fields等も拡張可能)
‘ OrganizerItemCopy Source, Destination, Name, Object
Application.OrganizerItemCopy Source:=mptPath, _
Destination:=targetPrj.FullName, _
Name:=””, _
Object:=pjOrganizerObjectViews

If Err.Number <> 0 Then
‘ ログ記録等のフォールバック処理をここに記述
Debug.Print “Organizer Sync Warning: ” & Err.Description
End If
On Error GoTo 0
End Sub

4. チーフアーキテクトの視点:メモリ管理とガバナンスの極意

このソリューションを大規模組織に導入する際、以下の設計思想を厳守しなければならない。

オブジェクトのライフサイクルと解放の徹底

VBAにおける `CreateObject` や `Application.Projects.Open` は、COMの参照カウンタを確実にインクリメントする。
スクリプトの途中で例外(Error)が発生した際、不完全な参照がメモリ上に残り続けると、MS Projectのプロセスがバックグラウンドでゾンビ化し、次回のファイルオープン時に深刻なファイルロックエラーを引き起こす。
必ず `On Error GoTo CleanUp` イディオムを使用し、ローカル変数のオブジェクト(`fso`, `prj` 等)の解放(`Set … = Nothing`)を担保せよ。

レガシー環境と32bit/64bitの混在対策

現在のエンタープライズでも、一部のサードパーティ製アドインが32bit依存であるケースや、逆に完全64bitへ移行しているケースが混在する。
コード内の `#If VBA7 Then` による条件付きコンパイルと `PtrSafe` 宣言は、APIクラッシュを防ぐための必須要件である。文字列ポインタの渡害には `StrPtr()` を用い、メモリレイアウトの不整合によるバッファオーバーランを未然に防いでいる点に注目してほしい。

システム間連携(外部基幹システムからのキック)

このVBAスクリプトを単体で動かすのではなく、外部のC#/.NETアプリケーションやPowerShellランチャーから、コマンドライン引数経由でMS Projectを起動し、自動実行プロシージャ(`Auto_Open` など)と組み合わせることで、「ユーザーの意識を介在させない完全自動ガバナンス・パイプライン」が完成する。
ユーザーがどの端末から作業しようとも、常に最新の公式MPT定義が適用された状態でプロジェクトが開始されるため、「ローカル環境の差異によるデータ破損」というエンジニアの永続的な悪夢から完全に解放されるのだ。

妥協のないコードと厳格なアーキテクチャ設計こそが、Project VBAを真のエンタープライズ・ツールへと昇華させる唯一の道である。

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