【実務・中級編】【初心者向け】読み取り専用で開いたプロジェクトを、別名で自動保存して編集作業を開始する自動化フロー – Project VBA解析バイブル

スポンサーリンク

【Project VBA極限解説】「読み取り専用」の罠を断つ!元ファイルを死守する安全自動保存ルーチンの構築

開発現場において、最も許されないヒューマンエラーの筆頭が「共有されているマスタスケジュール(元ファイル)への偶発的な上書き」である。

「ちょっと確認するだけのつもりで開いて、そのまま編集して保存してしまった……」
「ベースラインが狂い、プロジェクト全体の進捗管理が崩壊した……」

このようなインシデントは、個人の注意力をあてにしているうどん屋の看板のようなセキュリティ体制では防げない。システム側で強制的にガードをかけるべきだ。

今回は、Project VBAを用いて「読み取り専用で開かれたプロジェクトを検知し、瞬時に安全な別名で自動保存した上で、そのまま編集作業に移行できる堅牢な起動ルーチン」の設計と実装を伝授する。

1. なぜ「単なるファイルオープン」では事故を防げないのか

一般的なVBAエンジニア崩れがやりがちな悪手は、`Workbook_Open`のようなイベント駆動をProjectでもそのまま適用しようとすることだ。

しかし、Microsoft Projectのオブジェクトモデルとライフサイクルは、ExcelやWordとは一線を画す。
Projectには「グローバルテンプレート(Global.mpx / Global.ptm)」が存在し、プロジェクトファイル(`.mpp`)が開かれるコンテキストやタイミングによって、イベントの挙動が不安定になる特有の癖がある。

特に、ファイルを「読み取り専用(Read-Only)」で開いた場合、ユーザーがうっかり「上書き保存」を行おうとした瞬間にExcelのような親切な警告ダイアログが出ず、そのままエラーで落ちるか、最悪の場合はローカルのキャッシュと競合してファイルが破損する。

したがって、我々が目指すべきアプローチはこうだ。
1. ファイルを開いた瞬間に、読み取り専用フラグ(ReadOnly)をコードで検知する。
2. 読み取り専用であれば、即座に今日の日付や担当者名を付与したワーキングファイルとして自動保存する。
3. ユーザーには「安全な別名で作業を開始しました」と通知し、元ファイルを物理的に保護する。

2. 実装における3つの技術的ポイント

プロダクションコードを組むにあたり、以下の3点を押さえておかなければ実用に耐えない。

1. ファイルシステムの動的パス解決: 保存先フォルダが存在しない場合のエラーハンドリングと、パスの結合(`Environ`や`FileSystemObject`の活用)。
2. 上書き確認ダイアログのサイレント化: 自動保存時に同名ファイルが存在する場合の確認プロンプトを抑制し、安全にロールオーバーまたは別名付与する設計。
3. エラーバッファの確保: Project特有のファイルロック状態下での例外処理。

3. 【コピペ即実践】堅牢な自動保存・起動ルーチン

以下のコードは、ProjectのThisProjectモジュール(または標準モジュール)に配置し、ファイルオープン時に自動実行、あるいはリボンから手動トリガーできるように設計されたプロダクションコードだ。

‘ ==============================================================================
‘ モジュール名: modSafeOpenGuard
‘ 概要 : 読み取り専用オープンを検知し、安全な別名保存へ誘導する自動化ルーチン
‘ 著作権表記 : Enterprise Project Automation Architecture
‘ ==============================================================================
Option Explicit

Public Sub InitializeWorkingProject()
Dim prj As Project
Set prj = ActiveProject

‘ 1. プロジェクトが未保存(新規作成など)の場合は対象外とする
If prj.FullName = “” Then Exit Sub

‘ 2. 読み取り専用で開かれているか判定
If IsReadOnly(prj) Then
Call ExecuteSafeSaveAs(prj)
Else
‘ 編集可能モードの場合は通常の注意喚起のみ、あるいはそのままスルー
MsgBox “このプロジェクトは「編集可能モード」で開かれています。” & vbCrLf & _
“マスタへの直接変更には十分注意してください。”, vbInformation, “安全起動ガード”
End If

End Sub

‘ ——————————————————————————
‘ 内部関数: 読み取り専用フラグの判定
‘ ——————————————————————————
Private Function IsReadOnly(targetPrj As Project) As String
On Error GoTo ErrorHandler

‘ ProjectオブジェクトのReadOnlyプロパティを評価
IsReadOnly = targetPrj.ReadOnly
Exit Function

ErrorHandler:
‘ 評価失敗時は安全のためTrue(読み取り専用扱い)を返す
IsReadOnly = True
End Function

‘ ——————————————————————————
‘ 内部処理: 安全な別名保存の実行
‘ ——————————————————————————
Private Sub ExecuteSafeSaveAs(targetPrj As Project)
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)

Dim originalPath As String
Dim originalFolder As String
Dim originalName As String
Dim ext As String
Dim newFileName As String
Dim newFullPath As String

originalPath = targetPrj.FullName
originalFolder = fso.GetParentFolderName(originalPath)
originalName = fso.GetBaseName(originalPath)
ext = fso.GetExtensionName(originalPath)

‘ 命名規則: [元のファイル名]_[YYYYMMDD]_[担当者名/作業用].[拡張子]
‘ ※環境変数からユーザー名を取得し、一意のワーキングファイル名を生成
Dim userName As String
userName = Environ(“USERNAME”)

newFileName = originalName & “_” & Format(Date, “YYYYMMDD”) & “_” & userName & “_Working.” & ext
newFullPath = originalFolder & “\” & newFileName

‘ ユーザーへのインテンション確認
Dim msgRes As VbMsgBoxResult
msgRes = MsgBox(“このプロジェクトは「読み取り専用」で開かれました。” & vbCrLf & _
“元ファイルの破損・誤上書きを防ぐため、以下の作業用ファイルとして自動保存します。” & vbCrLf & vbCrLf & _
“保存先: ” & newFullPath & vbCrLf & vbCrLf & _
“今すぐこの別名ファイルで作業を開始しますか?”, _
vbYesNo + vbExclamation, “【Project安全起動】別名保存の確認”)

If vbYes = msgRes Then
On Error GoTo SaveError

‘ 警告メッセージ等を抑制して強制保存
Application.DisplayAlerts = False

‘ 別名で保存
targetPrj.SaveAs FileName:=newFullPath, FileFormat:=pjFileMPP

Application.DisplayAlerts = True

MsgBox “別名保存が完了しました。” & vbCrLf & _
“これより「” & newFileName & “」の編集を行ってください。”, vbInformation, “準備完了”
Exit Sub
Else
MsgBox “別名保存がキャンセルされました。読み取り専用のまま継続します。”, vbInformation, “キャンセル”
End If

Set fso = Nothing
Exit Sub

SaveError:
Application.DisplayAlerts = True
MsgBox “別名保存中に重大なエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “保存致命エラー”
Set fso = Nothing
End Sub

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

このコードをそのまま組み込むだけではなく、現場の運用ポリシーに合わせてさらに一段上の堅牢性を担保してほしい。

1. Auto_Openマクロとの併用注意
ProjectにはExcelのような`Workbook_Open`イベントが存在しないため、標準モジュールに`Auto_Open()`プロシージャを定義するか、グローバルテンプレートに組み込んでリボンからキックする設計にするのが定石だ。
2. ネットワークドライブ(SharePoint / OneDrive)上のパス解決
モダンな開発現場では、ファイルサーバーではなくCloud上のパス(`https://d.docs.live.net/…` など)でProjectを開くケースが増えている。その場合、`Scripting.FileSystemObject`がUNCパスやWebDAVパスの結合で予期せぬ挙動を示すことがある。クラウド環境が主体のチームであれば、パスの文字列置換処理を堅牢にラップするバリデーションを挟むべきだ。

「事故が起きてから対策する」のはアマチュアのすることだ。
プロのエンジニアであれば、「ユーザーがヒューマンエラーを起こしようのない構造」をコードで強制する。この自動保存ルーチンを導入し、あなたのプロジェクト管理環境を鉄壁のものへと引き上げてほしい。

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