【テクニカル・上級編】【初心者向け】保存時に「作成者」や「会社名」を社内規定通りに自動更新するマクロ – Project VBA解析バイブル

スポンサーリンク

【Project VBAを掌握する極限の知見】ファイルプロパティの強制調停:メタデータ自動同期アーキテクチャ

レガシーとモダンが混在する企業インフラストラクチャにおいて、ドキュメントのメタデータ管理は常にシステム管理者の頭痛の種である。特にMicrosoft Project(MPT/MPD/MPP)における「作成者」や「会社名」といったドキュメントプロパティは、属人化された手動入力に依存しているがために、監査証跡の欠落や、外部提出時のガバナンス違反を引き起こす温床となっている。

本稿では、Project VBAのオブジェクトライフサイクルを極限まで理解した上で、ファイル保存イベントをフックし、組織の標準フォーマットへとメタデータを強制的に同期・調停する実戦的アーキテクチャを解説する。

1. Project VBAにおけるドキュメントプロパティの構造的罠

一般的に、Office VBAにおけるプロパティ操作といえば `BuiltinDocumentProperties` コレクションを思い浮かべるだろう。しかし、Microsoft Projectのアーキテクチャは、ExcelやWordといった他のOfficeスイートとは一線を画す。

Projectの内部データベース構造において、メタデータは単なる文字列の保持領域ではない。ファイル保存時のI/Oバッファ、セキュリティコンテキスト、そしてOLE2コンパウンドドキュメントのストリーム構造に深く結びついている。

ここで、安易なプロパティ書込を行うと以下の問題が生じる:

  • ダーティフラグの誤認: 保存処理中にプロパティを書き換えることで、無限の保存ループや予期せぬ「保存しますか?」ダイアログの発生。
  • メモリリーク: ProjectのCOMオブジェクトモデルは厳密な参照管理を要求するため、不適切な変数スコープはメモリ断片化を招く。

これらを完全に制御するためには、アプリケーションレベルのイベントハンドラと、COMの解放作法を徹底したコード設計が不可欠である。

2. 実装:イベントドリブン・メタデータ調停エンジン

以下のコードは、プロジェクトの保存時(`ProjectBeforeSave`)に割り込み、社内規定のフォーマットへ「作成者」と「会社名」を強制上書きする実装である。

このコードは、標準モジュールではなく、必ず `ThisProject` クラスモジュール または専用のイベントハンドラクラスに実装する必要がある。

‘ =================================================================ライセンス・ヘッダ
‘ Project Architecture Governance Module: 0x01_MetadataEnforcer
‘ Author: Chief Architect
‘ Description: Projectファイルの保存トリガーを監視し、組織標準のメタデータを強制適用する
‘ =================================================================

Option Explicit

‘ アプリケーションイベントをフックするための宣言
Private WithEvents AppEvents As Application

‘ クラス初期化時にイベントをバインド
Private Sub Class_Initialize()
Set AppEvents = Application
End Sub

‘ 保存前イベントのインターセプト
Private Sub AppEvents_ProjectBeforeSave(ByVal pj As Project, ByVal SaveAsUI As Boolean, Cancel As Boolean)
On Error GoTo ErrorHandler

‘ デバッグおよびパフォーマンス最適化のための画面描画停止
Application.ScreenUpdating = False

‘ メタデータの強制上書き実行
Call EnforceCorporateMetadata(pj)

Exit Sub

ErrorHandler:
‘ 致命的なエラーを捕捉し、システムの強制終了を防ぎつつログを出力
MsgBox “Metadata Enforcement Error: ” & Err.Description, vbCritical, “Architecture Governance”
‘ 必要に応じてCancel = Trueを設定して保存を中断する設計も可能
‘ Cancel = True

Application.ScreenUpdating = True
End Sub

‘ メタデータ調停のコアロジック
Private Sub EnforceCorporateMetadata(ByVal targetProject As Project)
Dim builtInProps As Office.DocumentProperties
Dim authorName As String
Dim companyName As String

‘ ガバナンス要件に基づく標準値の取得(Active Directoryや環境変数からの取得に拡張可能)
authorName = GetSystemAuthorIdentity()
companyName = “Enterprise PMO Governance Office”

‘ Projectの BuiltinDocumentProperties へのアクセス
‘ 注: Microsoft Projectの型ライブラリ依存関係を排除するため、動的バインディングまたはOffice Object Libraryを参照
On Error Resume Next
Set builtInProps = targetProject.BuiltInDocumentProperties
On Error GoTo 0

If builtInProps Is Nothing Then
Err.Raise 50001, “EnforceCorporateMetadata”, “BuiltInDocumentProperties コレクションの取得に失敗しました。”
End If

‘ プロパティの強制書き換え(存在しない場合はエラーになるため個別にトラップ)
SetPropertySafe builtInProps, “Author”, authorName
SetPropertySafe builtInProps, “Company”, companyName
SetPropertySafe builtInProps, “Last Author”, authorName

‘ オブジェクトの明示的解放(メモリ最適化の極意)
Set builtInProps = Nothing
End Sub

‘ 安全なプロパティ設定ラッパー関数
Private Sub SetPropertySafe(ByRef props As Office.DocumentProperties, ByVal propName As String, ByVal propValue As String)
Dim prop As Office.DocumentProperty
On Error Resume Next
Set prop = props(propName)
If Err.Number <> 0 Then
‘ プロパティが存在しない場合は新規追加
Err.Clear
props.Add name:=propName, LinkToProperty:=False, Type:=msoPropertyTypeString, Value:=propValue
Else
‘ 存在する場合は値を更新
prop.Value = propValue
End If
On Error GoTo 0
Set prop = Nothing
End Sub

‘ 実行主体(作成者)のアイデンティティを解決するスタブ(API連携への拡張ポイント)
Private Function GetSystemAuthorIdentity() As String
‘ 実際のエンタープライズ環境では、ここでWin32 API (GetUserNameEx等) を呼び出し、
‘ 正確なWindowsログインIDやCN(Common Name)を取得する。
GetSystemAuthorIdentity = Environ$(“USERNAME”) & ” [Governance-Managed]”
End Function

3. チーフアーキテクトが解説するコードの急所

① `WithEvents` によるライフサイクルの掌握

グローバルなイベントプロシージャに頼る古いVBAの記述方法は、メモリリークや意図しないスコープ汚染の温床となる。`Class_Initialize` でインスタンス化された `AppEvents` は、プロジェクトのライフサイクルと完全に同期し、不要になった時点でガベージコレクトされる設計にしている。

② 明示的なオブジェクトの解放 (`Set … = Nothing`)

VBAのランタイムは参照カウント方式を採用しているが、COMオブジェクト(特にOfficeのDocumentPropertiesやDocumentProperty)をローカル変数に保持したまま放置すると、Excel/Projectプロセス内にCOM RCW (Runtime Callable Wrapper) の残骸が残り、ファイルロックやメモリ肥大化を引き起こす。
本コードでは、処理の直後に `Set builtInProps = Nothing` および `Set prop = Nothing` を実行し、即座にメモリポインタを解放している。

③ 例外のハンドリングとフェイルセーフ

保存イベント内でのエラーは、最悪の場合「プロジェクトファイルが永久に保存できなくなる」という致命的なインシデントに繋がる。そのため、`On Error Resume Next` を極小のスコープに限定して使用し、万が一メタデータの書き込みに失敗した場合でも、保存処理自体を止めるか、ログを残してスルーするかの制御権を担保している。

4. エンタープライズ展開への展望:レガシーの先へ

このVBAマクロ単体でもローカルガバナンスとしての効果は絶大であるが、真のシニアエンジニアであれば、これを起点としたシステム間連携を視野に入れるべきだ。

  • Active Directory / LDAP連携: `GetSystemAuthorIdentity()` の部分を拡張し、ドメインコントローラーから正確な部署名とフルネームを動的に取得する。
  • CI/CDパイプラインとの統合: サーバーサイドで動くVB.NET / PowerShellスクリプト(COMオートメーション経由でのProject操作)から同様のロジックを流し込み、Gitリポジトリに格納される前の段階でメタデータを完全にクレンジングする。

手動による運用ルールは必ず形骸化する。コードによって「ルールを強制する仕組み」を構築することこそが、真にスケーラブルなプロジェクト管理基盤の第一歩である。

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