Project VBAを掌握する極限の知見:保存時の「プロジェクト概要」自動化で属人化を断つ
こんにちは。エンタープライズ領域におけるプロジェクト管理基盤の構築や、数千規模のスケジュールデータを裏で操る自動化アーキテクトとして、日々VBAと向き合っている。
現場でよく耳にする不満がある。
「また、プロジェクト情報の更新を忘れて上長に差し戻された」
「ファイルのプロパティにある『作成者』や『最終更新日』がバラバラで、どれが最新のマスターデータか分からない」
Microsoft Project(MSP)の運用において、ファイルのプロパティ(プロジェクトの概要)を手動でメンテし続けるのは、エンジニアリングの観点から見て完全な悪手である。人間は忘れる生き物であり、手動作業が介在するプロセスは必ず破綻する。
今回は、Project VBAを用いて「ファイル保存時に、最新の更新者とタイムスタンプを自動的にプロジェクト情報へ刻み込む」堅牢な定型化マクロの設計思想と実装を授けよう。
—
1. なぜ「手動保存」に頼る設計が破綻するのか
多くの現場では、プロジェクトマネージャー(PM)が退勤間際に「保存」ボタンを押す。その際、ドキュメントのプロパティ(タイトル、管理者、会社名、コメントなど)まで意識を回せる者は少ない。
しかし、PMO(プロジェクトマネジメントオフィス)や複数チームでの連携において、「このスケジュールデータは誰が、いつ時点の状態で保存したものか」のトレースは生命線となる。
これをVBAで解決する場合、素人が書くと単に `ActiveProject.FileSave` の前後にコードを挟むだけの脆いマクロになりがちだ。
だが、プロのアーキテクトが目指すべきは、「ユーザーがどのような経路で保存しようとも(上書き保存、名前を付けて保存、終了時の保存プロンプト)、確実にメタデータが同期される仕組み」である。
ここで鍵となるのが、Projectのイベントハンドリングと、組み込みプロパティ(BuiltInDocumentProperties)の正しいライフサイクル管理だ。
—
2. 実装のアーキテクチャと設計上の注意点
Project VBAには、アプリケーションレベルやプロジェクトレベルのイベントを捉える仕組みがある。今回使用するのは `ProjectBeforeSave` イベントだ。
しかし、ここで一つ重要な注意点がある。
「イベント内でファイルを保存(FileSave等)すると、イベントが無限ループを引き起こす」という罠だ。
保存しようとする瞬間にイベントが発火し、その中でさらに保存コマンドを実行すれば、スタックオーバーフローか予期せぬエラーでMSP自体がクラッシュする。
したがって、設計の鉄則は以下の通りだ。
1. イベント内ではドキュメントのプロパティ(オブジェクト)の書き換えのみを行う。
2. 実際の保存処理そのものはユーザーの操作(あるいは標準の保存シーケンス)に委ねる。
3. エラーハンドリングを完璧にし、万が一プロパティの書き込みに失敗しても保存プロセス自体を阻害しないようにする。
—
3. プロダクションコード:コピペで即座に機能する堅牢な実装
それでは、実際のコードを公開する。
このコードは、グローバルテンプレート(Global.mpt)または対象のプロジェクトファイル(.mpp)の `ThisProject` クラスモジュールに配置することで機能する。
ステップ1: イベント用クラスモジュールの設定
`ThisProject` モジュールを開き、以下のコードをそのまま貼り付けてほしい。
‘ ==============================================================================
‘ モジュール名: ThisProject (クラスモジュール)
‘ 概要: プロジェクト保存時に「プロジェクトの概要」プロパティを自動更新する
‘ ==============================================================================
Option Explicit
‘ イベントを有効にするための変数宣言
Private WithEvents AppEvents As Application
Private Sub Project_Open(ByVal Sv As Project)
‘ アプリケーションイベントのバインド
Set AppEvents = Application
End Sub
Private Sub Project_Close(ByVal Sv As Project)
‘ メモリ解放
Set AppEvents = Nothing
End Sub
‘ ——————————————————————————
‘ イベント名: AppEvents_ProjectBeforeSave
‘ 概要: プロジェクトが保存される直前に自動発火し、メタデータを更新する
‘ ——————————————————————————
Private Sub AppEvents_ProjectBeforeSave(ByVal pj As Project, ByRef Cancel As Boolean)
On Error GoTo ErrorHandler
Dim userName As String
Dim updateTime As String
‘ 1. 実行環境から現在のユーザー名を取得(環境に合わせてEnviron変更可)
userName = Environ$(“USERNAME”)
If userName = “” Then userName = “Unknown User”
‘ 2. タイムスタンプの生成(ISO 8601に近い形式で可読性を担保)
updateTime = Format$(Now, “YYYY-MM-DD HH:MM:SS”)
‘ 3. 組み込みドキュメントプロパティの更新
‘ ※存在しないプロパティにアクセスするとエラーになるため、エラーハンドリング内で処理
With pj
‘ 「管理者 (Manager)」プロパティに最終更新者を記録
Call SetDocumentProperty(pj, “Manager”, userName)
‘ 「コメント (Comments)」プロパティに最終更新日時と自動更新の旨を記録
Call SetDocumentProperty(pj, “Comments”, “Last updated by ” & userName & ” at ” & updateTime)
‘ 必要に応じて「会社 (Company)」や「カテゴリ (Category)」も制御可能
‘ Call SetDocumentProperty(pj, “Company”, “Enterprise PMO Div.”)
End With
Exit Sub
ErrorHandler:
‘ 予期せぬエラーが発生した場合でも、プロジェクト自体の保存をブロックしない設計とする
MsgBox “プロジェクト概要の自動更新中に軽度なエラーが発生しました: ” & Err.Description, _
vbExclamation + vbOKOnly, “メタデータ自動更新ガード”
Resume Next
End Sub
‘ ——————————————————————————
‘ 補助プロシージャ: ドキュメントプロパティを安全に設定・追加する
‘ ——————————————————————————
Private Sub SetDocumentProperty(ByVal pj As Project, ByVal propName As String, ByVal propValue As String)
On Error GoTo AddNew
‘ 既存のプロパティが存在する場合は値を更新
pj.BuiltinDocumentProperties(propName).Value = propValue
Exit Sub
AddNew:
‘ プロパティが存在しない(初期状態等)場合は新しく追加する
On Error GoTo CreateError
pj.BuiltinDocumentProperties.Add Name:=propName, _
LinkToContent:=False, _
Type:=msoPropertyTypeString, _
Value:=propValue
Exit Sub
CreateError:
‘ プロパティ追加に失敗した場合は無視(ログ出力等に置き換えても良い)
Resume Next
End Sub
—
4. このコードが「現場で生き残る」理由
素人が書いたVBAコードは、少しの環境変化や例外データで簡単にハングアップする。しかし、上記のコードにはアーキテクトとしてのシビアな配慮が組み込まれている。
1. 無限ループの完全回避: `FileSave` メソッドをコード内から呼び出さず、`BeforeSave` イベントのフックだけに留めているため、MSPのネイティブな保存プロセスを阻害しない。
2. フェイルセーフ設計 (`On Error GoTo`): 万が一、社内ネットワークの切断やExcel/COMオブジェクトの競合によってプロパティ操作に失敗しても、`Resume Next` により「ファイルの保存そのものが失敗する」という最悪の事態を防ぐ。プロジェクトデータが保存できない絶望感を、このコードは確実に回避する。
3. 動的なプロパティ生成: `BuiltinDocumentProperties` は、ファイルの状態によっては特定のプロパティ(例: `Manager` や `Comments`)が初期化されていないことがある。存在チェックと動的追加(`Add` メソッド)をラップすることで、どのような新規ファイルであってもエラーなく動作する。
—
5. 運用への組み込みと展開のポイント
このマクロを組織全体に展開する場合、個々の `.mpp` ファイルに埋め込むのはナンセンスだ。メンテナンス性が最悪になる。
- グローバルテンプレート(Global.mpt)への配備:
企業内の標準PCの `Global.mpt` にこの `ThisProject` コードを組み込んでおくことで、全社で作成されるすべての新規プロジェクトファイルにおいて、意識することなくこの自動ガバナンスが効くようになる。
- PMOとしてのガバナンス強化:
「誰が最後に手を加えたか」がファイルプロパティの「管理者」「コメント」欄に強制的に刻まれるため、ファイルサーバー上で散乱する類似タイトルのスケジュールデータも、プロパティを覗くだけで真贋判定が可能になる。
手動による無駄なオペレーションは、エンジニアリングの力で徹底的に駆逐せよ。
あなたのプロジェクト管理の現場が、無駄なストレスから解放され、真に価値のあるスケジュール統制に集中できることを願う。
