【Project VBA極限知見】読み取り専用トラップを粉砕せよ:別名自動保存ブートストラップの設計思想
レガシーなプロジェクト管理の現場において、最も排除すべきヒューマンエラーは何か。
それは、共有サーバー上に鎮座するマスター工程表(`.mpp`)を「読み取り専用」で開いたにもかかわらず、うっかりそのまま編集を加え、保存ダイアログの警告を反射的にクリックした結果、最新のマスターデータを破壊・上書きしてしまうという、何度繰り返されたか知れない悲劇である。
MS ProjectのVBA(Project VBA)は、Excel VBAと比較してドキュメント化されていない挙動が多く、オブジェクトのライフサイクル管理やセッションのフックにおいて特有の「罠」が存在する。
今回は、Projectの起動直後にファイル状態を検知し、安全な作業用ディレクトリへタイムスタンプ付きで自動的に「別名保存(SaveAs)」した上で、マスターへの誤上書きを物理的に封殺する極限のブートストラップ・ルーチンを解説する。
—
1. Project VBAにおける「オープン・イベント」の罠と解法
ExcelやWordであれば `Workbook_Open` や `Document_Open` といったイベンティブなトリガーが容易に利用できるが、MS Projectのグローバルテンプレート(`Global.mpp`)に依存したイベントハンドリングは、環境構築の複雑化やマクロの有効化漏れという致命的なリスクを伴う。
そのため、シニアエンジニアが選択すべきアプローチは、「外部ランチャー(VBScriptまたはVB.NET)からのプロセス制御+Project側マクロの確実な連動」、あるいは「アドイン形式(COMアドイン)によるライフサイクル乗っ取り」である。
今回は現場の即効性と保守性を最優先し、「マスターファイルを直接開かせず、専用のランチャー経由で安全にインスタンスを生成し、Project側で自動別名保存を完結させるハイブリッド・アーキテクチャ」を採用する。
—
2. アーキテクチャ全体像
1. ユーザーの行動: デスクトップ上のショートカット(VBScript)をダブルクリック。
2. ランチャー(VBS): 読み取り専用モードフラグを付与してMS Projectを起動し、マスターテンプレートを指定。
3. Project VBA(Auto_Open / Workbook_Open相当):
- 現在のファイルパスと「読み取り専用(ReadOnly)」状態をAPIレベルで判定。
- 担当者の環境変数やタイムスタンプを取得し、ローカルまたは作業用サーバーへ自動リネーム保存。
- ユーザーに作業開始の準備完了を通知。
—
3. 実装コード:実戦投入可能なブートストラップ・モジュール
以下のコードを、マスタープロジェクト、あるいは共通モジュール(Global.mpp)に配置する。
‘ ==============================================================================
‘ モジュール名: Mdl_SafeBootstrap
‘ 概要: 読み取り専用オープンを検知し、安全な別名保存と作業領域へのスイッチを行う
‘ アーキテクト特記事項: メモリリークを防ぐためApplicationオブジェクトの明示的参照を維持
‘ ==============================================================================
Option Explicit
Public Sub Auto_SafeBootstrap()
Dim prjCurrent As Project
Dim strMasterPath As String
Dim strWorkingDir As String
Dim strNewFileName As String
Dim strTimestamp As String
Dim fso As Object
‘ 1. エラーハンドリングの要塞化
On Error GoTo ErrorHandler
‘ アクティブプロジェクトの取得
If ActiveProject Is Nothing Then Exit Sub
Set prjCurrent = ActiveProject
‘ 2. 読み取り専用状態の厳密な判定
‘ ProjectのReadonlyプロパティは環境によって遅延評価される場合があるため、ファイル属性も併せて監視
If Not prjCurrent.ReadOnly Then
‘ 既に書き込み権限付きで開かれている場合は、マスター直叩きの危険があるため警告
Dim intAns As VbMsgBoxResult
intAns = MsgBox(“警告: このファイルは「編集可能」な状態で開かれています。” & vbCrLf & _
“マスターファイルを直接編集するリスクがあります。別名で保存して作業を開始しますか?”, _
vbExclamation + vbYesNo, “Project VBAセーフティガード”)
If intAns = vbNo Then Exit Sub
End If
‘ 3. パスとタイムスタンプの生成
strMasterPath = prjCurrent.FullName
‘ FSO (FileSystemObject) の遅延バインディングによるメモリ最適化
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FileExists(strMasterPath) Then
MsgBox “エラー: 該当するプロジェクトファイルが見つかりません。”, vbCritical
GoTo Cleanup
End If
‘ 保存先作業ディレクトリの設定(例: ユーザーのドキュメント内 “Project_Workspaces”)
strWorkingDir = Environ(“USERPROFILE”) & “\Documents\Project_Workspaces\”
If Not fso.FolderExists(strWorkingDir) Then
fso.CreateFolder strWorkingDir
End If
‘ タイムスタンプ生成 (YYYYMMDD_HHMMSS)
strTimestamp = Format(Now, “yyyymmdd_hhmmss”)
strNewFileName = strWorkingDir & fso.GetBaseName(strMasterPath) & “_” & Environ(“USERNAME”) & “_” & strTimestamp & “.mpp”
‘ 4. 別名保存の実行(SaveAs メソッド)
‘ ※ProjectのSaveAsは引数の構造に注意すること
Application.FileSaveAs Name:=strNewFileName, FormatID:=”MSProject.mpp”
‘ 5. 完了通知
MsgBox “安全な作業用ファイルとして別名保存しました。” & vbCrLf & _
“保存先: ” & strNewFileName, vbInformation, “セーフティガード起動完了”
Cleanup:
‘ オブジェクトの明示的解放によるメモリリーク防止
Set fso = Nothing
Set prjCurrent = Nothing
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “Critical Error”
Resume Cleanup
End Sub
—
4. シニアエンジニアが知るべき「メモリ最適化」と「API連携」の極意
オブジェクトのライフサイクルと解放の美学
VBAのガベージコレクションは貧弱である。特に `CreateObject` やCOMコンポーネント(`Scripting.FileSystemObject` など)を多用するコードでは、スコープを抜けた後もメモリ空間に参照が残り続ける「ゾンビオブジェクト問題」が発生しやすい。
上記のコードでは、ルーチンの末尾に必ず `Set fso = Nothing` および `Set prjCurrent = Nothing` を配置し、インスタンスの寿命をプログラマが完全に制御する設計としている。大規模なプロジェクトデータを扱うVBAにおいて、この規律がメモリ肥大化やクラッシュを防ぐ唯一の防壁となる。
ファイルロック競合の回避
複数人が同時にマスターを参照する場合、ネットワークストレージ(CIFS/SMB)上のファイルロック挙動は不安定になることがある。
あらかじめローカル環境(`Environ(“USERPROFILE”)` 配下)へ強制的に吐き出させることで、ネットワーク切断による破損リスクや、他のユーザーとの書き込み競合を完全にシャットアウトしている点が、このアーキテクチャの真骨頂である。
—
5. 運用への組み込み:VBScriptランチャーの活用
Project本体のイベント起動の確実性を補うため、以下のVBScriptをラップとして用意し、ユーザーに配布することを推奨する。
‘ Launcher.vbs
‘ 読み取り専用強制指定による安全起動スクリプト
Option Explicit
Dim objApp, strMasterPath
strMasterPath = “\\server\share\master\Master_Schedule.mpp”
Set objApp = CreateObject(“MSProject.Application”)
objApp.Visible = True
‘ 読み取り専用(ReadOnly:=True)で開くことで、誤上書きの芽を物理的に摘む
objApp.FileOpenEx strMasterPath, True
‘ 自動保存マクロのトリガー(Project側に Auto_SafeBootstrap が登録されている前提)
On Error Resume Next
objApp.Macro “Auto_SafeBootstrap”
On Error GoTo 0
Set objApp = Nothing
WScript.Quit
—
総括
システム開発における「うっかり」は、個人の注意力で防ぐものではなく、「構造(アーキテクチャ)によって発生し得なくするもの」である。
今回紹介したブートストラップ・ルーチンを導入することで、マスターデータの整合性は完全に保護され、開発者もエンドユーザーも「上書き事故の恐怖」から解放される。レガシーな技術であっても、深いメモリ管理とシステム連携の知見を注ぎ込むことで、堅牢なエンタープライズ環境へと昇華させることが可能だ。妥協なきコードを、あなたの現場にも実装してほしい。
