【入門編】【中級者向け】読み込み専用で開いたファイルの「名前を付けて保存」による上書き防止策 – Project VBA解析バイブル

スポンサーリンク

ようこそ、Project VBAの世界へ。伝説のチーフアーキテクト、タスクです。

今回は、業務自動化の現場で「データ保全」という最も重要なテーマの一つに深く切り込む、実践的なテクニックをご紹介します。単なるファイルの開閉ではありません。Projectファイルを「読み込み専用」で開くことで、誤操作による元データの破損を防ぎ、さらにその編集結果を「別名で保存」する一連のフローをVBAで自動化する。これぞ、ベテランエンジニアが魂を込めて設計する、堅牢な運用の一歩です。

プログラミング初学者の方も、マクロ記録から一歩踏み出したい方も、ご安心ください。一つ一つのステップを丁寧に、そして本質的な意味を噛み砕いて解説します。「ここをクリアすれば、Project VBAの基本はバッチリですよ」と、私が自信を持って言える、そんな極意を伝授しましょう。

データは命!なぜ「読み込み専用」で開くのか?

プロジェクト管理において、計画データは会社の未来を映す鏡であり、チームの羅針盤です。もし、この重要なデータが不用意な操作で上書きされたり、意図しない形で変更されてしまったらどうなるでしょうか? 最悪の場合、プロジェクトの遅延、予算超過、そして信頼の失墜に繋がりかねません。

だからこそ、私たちは「データ保全」を徹底しなければなりません。その第一歩が、既存ファイルを「読み込み専用」で開くという考え方です。

まるで、博物館で貴重な古文書をガラスケース越しに眺め、その内容をノートに書き写すようなイメージです。直接古文書に手を触れて書き換えるリスクを完全に排除する。これが「読み込み専用」の真髄です。

そして、その書き写した内容(編集結果)は、必ず新しいノート(別ファイル)に保存する。こうすることで、万が一の失敗があっても、オリジナルの古文書(元データ)は常に安全なまま保たれるわけです。

この堅牢な運用フローを、Project VBAを使って自動化していきましょう。

Project VBAでファイルを操る基本中の基本

Project VBAでファイルを操作する際には、主に以下の二つのメソッドを使用します。

  • `Application.FileOpen`:プロジェクトファイルを開く
  • `Application.FileSaveAs`:プロジェクトファイルを名前を付けて保存する

これらのメソッドが、今回の自動化の鍵を握ります。

`Application.FileOpen`:読み込み専用でファイルを開く

`FileOpen` メソッドは、Projectアプリケーションの心臓部である `Application` オブジェクトから呼び出します。様々な引数がありますが、特に重要なのは `ReadOnly` 引数です。

‘ 構文のイメージ
Set projectObject = Application.FileOpen(Name, ReadOnly, Merge, … 他の引数 …)

  • `Name` (必須): 開くプロジェクトファイルのフルパスを指定します。
  • `ReadOnly` (任意): ここを `True` に設定することで、ファイルを読み込み専用モードで開きます。これが今回の肝です。

ファイルを読み込み専用で開くと、Projectはファイルを変更しようとすると警告を発し、上書き保存を許可しません。これにより、元データへの不用意な変更が防がれます。

`Application.FileSaveAs`:名前を付けて保存する

`FileSaveAs` メソッドも `Application` オブジェクトから呼び出します。このメソッドを使うことで、開いているプロジェクトを新しい名前や場所で保存できます。

‘ 構文のイメージ
Application.FileSaveAs(Name, Format, ReadOnlyRecommended, … 他の引数 …)

  • `Name` (必須): 新しいプロジェクトファイルのフルパスを指定します。
  • `Format` (任意): 保存するファイルの形式を指定できます(例: `pjMPP` でProjectファイル形式、`pjXML` でXML形式など)。通常は省略すれば `.mpp` 形式で保存されます。

このメソッドの重要な挙動として、新しい名前で保存されたファイルが、Projectアプリケーションの「アクティブなプロジェクト」になります。そして、元の読み込み専用で開いたファイルは、閉じられずにバックグラウンドで開いたままになります。この挙動が、後ほど解説する「オブジェクトのライフサイクル」において非常に重要になります。

【極限の知見】読み込み専用ファイルの上書き防止と別名保存の自動化

さあ、いよいよ本丸です。具体的なVBAコードを通じて、この堅牢な運用フローを構築していきましょう。

目標とする運用フロー

1. ユーザーに、編集したい既存のProjectファイルを選択させる。
2. 選択されたファイルを「読み込み専用」で開く。
3. 開いたファイルのコピーを、自動的に生成された新しいファイル名(例: `元のファイル名_YYYYMMDD_HHMMSS.mpp`)で「名前を付けて保存」する。
4. 元の読み込み専用ファイルは閉じる。
5. 新しく保存されたファイルで、ユーザーが引き続き編集作業を行えるようにする。

実践VBAコード

Sub OpenReadOnly_SaveAs_And_Continue()

‘ プロシージャ名: OpenReadOnly_SaveAs_And_Continue
‘ 目的 : 既存のProjectファイルを読み込み専用で開き、
‘ タイムスタンプ付きの別名で保存して編集を継続する運用を自動化します。
‘ これにより、元のファイルへの誤操作による上書きを完全に防止します。
‘ 開発者 : タスク (伝説のチーフアーキテクト)
‘ 最終更新日 : 2023-10-27

Dim prjApp As MSProject.Application ‘ Projectアプリケーションオブジェクト
Dim originalPrj As MSProject.Project ‘ 元の読み込み専用プロジェクトオブジェクト
Dim newPrj As MSProject.Project ‘ 新しく保存されたプロジェクトオブジェクト
Dim originalFilePath As String ‘ 元のプロジェクトファイルのフルパス
Dim originalFileNameWithoutExt As String ‘ 元のファイル名 (拡張子なし)
Dim newFileName As String ‘ 新しいプロジェクトファイル名
Dim newFolderPath As String ‘ 保存先のフォルダパス
Dim newFullFilePath As String ‘ 新しいプロジェクトファイルのフルパス
Dim originalDisplayAlerts As Boolean ‘ DisplayAlertsの元々の設定を保持するための変数

‘ ————————————————————————-
‘ 1. Projectアプリケーションのインスタンスを取得または起動する
‘ ————————————————————————-
‘ GetObject関数で、既に実行中のProjectインスタンスがあるか試みる。
‘ なければエラーが発生するので、On Error Resume Nextでエラーを無視する。
On Error Resume Next
Set prjApp = GetObject(, “MSProject.Application”)
On Error GoTo 0 ‘ エラー処理を元に戻す (重要!)

If prjApp Is Nothing Then
‘ 実行中のProjectがなければ、新しくProjectアプリケーションを起動する
Set prjApp = CreateObject(“MSProject.Application”)
prjApp.Visible = True ‘ Projectウィンドウを表示する (ユーザーが操作できるように)
‘ Tip: このマクロでProjectを起動した場合、マクロ終了時にProjectを終了させるか
‘ 判断するフラグを持たせることもできますが、今回はユーザーが続けて
‘ 作業することを想定し、開いたままにします。
End If

‘ ————————————————————————-
‘ 2. ユーザーに既存のプロジェクトファイルを選択させる
‘ ————————————————————————-
‘ Application.FileDialogを使って、ユーザーにファイルを選択させることで、
‘ パスを固定するよりも柔軟な運用が可能になります。
With prjApp.FileDialog(msoFileDialogFilePicker)
.AllowMultiSelect = False ‘ 複数ファイルの選択は許可しない
.Title = “読み込み専用で開くプロジェクトファイルを選択してください”
.Filters.Clear ‘ 既存のフィルターをクリア
.Filters.Add “Microsoft Project ファイル”, “.mpp” ‘ Projectファイルのみを表示
.Filters.Add “すべてのファイル”, “.” ‘ すべてのファイルも選択可能に
.FilterIndex = 1 ‘ デフォルトでProjectファイルを指定

‘ デフォルトの開始パスをユーザーの「ドキュメント」フォルダに設定
.InitialFileName = Environ(“USERPROFILE”) & “\Documents\”

If .Show = -1 Then ‘ ユーザーが「開く」ボタンをクリックした場合
originalFilePath = .SelectedItems(1) ‘ 選択されたファイルのフルパスを取得
Else
‘ ユーザーがキャンセルした場合の処理
MsgBox “ファイル選択がキャンセルされました。処理を中断します。”, vbInformation
Set prjApp = Nothing ‘ オブジェクトを解放 (Projectは開いたまま)
Exit Sub ‘ プロシージャを終了
End If
End With

‘ 選択されたファイルが存在するか最終確認 (FileDialogで選択しても念のため)
If Dir(originalFilePath) = “” Then
MsgBox “指定されたファイルが見つかりません: ” & originalFilePath, vbExclamation
Set prjApp = Nothing
Exit Sub
End If

‘ ————————————————————————-
‘ 3. ファイルを読み込み専用で開く準備 (DisplayAlertsの制御)
‘ ————————————————————————-
‘ DisplayAlertsを一時的にFalseに設定することで、ファイルを開く際などに
‘ 表示されうる警告ダイアログ(例: 読み取り専用で開きますか?など)を抑制します。
‘ これは非常に便利な機能ですが、副作用も大きいため、必ず元の設定に戻すことが重要です。
originalDisplayAlerts = prjApp.DisplayAlerts ‘ 現在のDisplayAlerts設定を保存
prjApp.DisplayAlerts = False ‘ 警告ダイアログを一時的に抑制

‘ ————————————————————————-
‘ 4. ファイルを読み込み専用で開く
‘ ————————————————————————-
‘ FileOpenメソッドは、開いたProjectオブジェクトを返します。
‘ これを originalPrj 変数に格納することで、元の読み込み専用ファイルへの
‘ 参照を明確に保持し、後のオブジェクトのライフサイクル管理に役立てます。
Set originalPrj = prjApp.FileOpen(originalFilePath, ReadOnly:=True)

‘ DisplayAlertsの設定を元に戻す (非常に重要!)
prjApp.DisplayAlerts = originalDisplayAlerts

‘ プロジェクトが開かれたことを確認 (エラーハンドリング)
If originalPrj Is Nothing Then
MsgBox “プロジェクトファイルを開けませんでした。” & vbCrLf & _
“ファイルが破損しているか、アクセス権限がない可能性があります。”, vbCritical
Set prjApp = Nothing
Exit Sub
End If

MsgBox “ファイル名: ” & originalPrj.Name & ” を読み込み専用で開きました。”, vbInformation

‘ ————————————————————————-
‘ 5. 新しいファイル名を生成し、名前を付けて保存する
‘ ————————————————————————-
‘ 元のファイル名から拡張子を除いた部分を取得
originalFileNameWithoutExt = Left(originalPrj.Name, InStrRev(originalPrj.Name, “.”) – 1)

‘ 新しいファイル名を生成 (例: 元のファイル名_YYYYMMDD_HHMMSS.mpp)
‘ Format(Now, …) で現在の日時を整形してファイル名に含めることで、
‘ ファイルのバージョン管理を容易にします。
newFileName = originalFileNameWithoutExt & “_” & Format(Now, “YYYYMMDD_HHMMSS”) & “.mpp”

‘ 保存先のフォルダパスを設定 (ここでは元のファイルと同じフォルダにする)
newFolderPath = Left(originalFilePath, InStrRev(originalFilePath, “\”)) ‘ 最後の”\”まで含める
newFullFilePath = newFolderPath & newFileName

‘ 名前を付けて保存を実行
‘ FileSaveAsメソッドは、保存した新しいファイルをアクティブプロジェクトとして設定します。
‘ この時、originalPrjが参照している元の読み込み専用ファイルは開いたままの状態になります。
prjApp.FileSaveAs Name:=newFullFilePath

‘ 新しく保存されたプロジェクトへの参照を取得
‘ FileSaveAsの挙動により、新しく保存されたファイルが現在アクティブになっているため、
‘ ActiveProjectプロパティでその参照を取得できます。
Set newPrj = prjApp.ActiveProject

‘ ————————————————————————-
‘ 6. 元の読み込み専用ファイルを閉じる (オブジェクトのライフサイクル管理の要点)
‘ ————————————————————————-
‘ ここが重要なポイントです。FileSaveAsを実行した後、originalPrjが参照する
‘ 元の読み込み専用ファイルはまだProjectアプリケーション内で開かれたままです。
‘ リソースの無駄遣いを防ぎ、ユーザーが複数の同名ファイルで混乱しないように、
‘ 明示的に閉じます。

‘ 警告ダイアログを一時的に抑制し、保存確認なしで閉じる
‘ (読み込み専用なので、保存されていない変更はそもそもありませんが念のため)
prjApp.DisplayAlerts = False
originalPrj.Close ‘ 元の読み込み専用プロジェクトを閉じる
prjApp.DisplayAlerts = originalDisplayAlerts ‘ DisplayAlertsの設定を元に戻す (重要!)

MsgBox “プロジェクトファイルが ” & newPrj.Name & ” として保存され、” & vbCrLf & _
“引き続きこの新しいファイルで編集作業ができます。”, vbInformation

‘ ————————————————————————-
‘ 7. オブジェクトの解放 (非常に重要!)
‘ ————————————————————————-
‘ 使用し終わったオブジェクトは、必ずSet Nothingで解放しましょう。
‘ これはメモリリークを防ぎ、パフォーマンスを最適化するための基本です。
‘ 特に、大規模なプロジェクトファイルや多数の操作を行う場合、この管理を怠ると
‘ プログラムが不安定になったり、Projectアプリケーション自体の動作が重くなったりします。
Set originalPrj = Nothing ‘ 元の読み込み専用プロジェクトへの参照を解放
Set newPrj = Nothing ‘ 新しいプロジェクトへの参照を解放

‘ Projectアプリケーションオブジェクト自体は、ユーザーが作業を続けるために
‘ 通常は解放せず開いたままにしておくのが一般的です。
‘ もしマクロ終了後にProjectアプリケーションを完全に終了させたい場合は、
‘ prjApp.Quit を呼び出す必要がありますが、今回は行いません。
‘ Set prjApp = Nothing ‘ アプリケーションオブジェクトの参照を解放するだけ。Projectは開いたまま。

End Sub

コード解説と「極限の知見」

このコードには、単なるファイル操作を超えた、業務自動化の設計思想が詰まっています。

1. Projectアプリケーションのインスタンス管理

`GetObject` と `CreateObject` を使い分けることで、既にProjectが起動している場合はそれを使い、起動していない場合は新しく起動します。これは、ユーザーのPC環境に合わせた柔軟な対応であり、無駄なリソース消費を防ぐスマートな設計です。

2. `Application.FileDialog` の活用

ファイルパスをコードに直接書き込むのではなく、`Application.FileDialog` を使うことで、マクロの汎用性が格段に上がります。これにより、異なるファイルや異なる環境でも同じマクロを再利用できるようになります。ユーザーインターフェースを提供することは、自動化されたツールが現場で受け入れられるための重要な要素です。

3. `Application.DisplayAlerts` の賢い利用

`DisplayAlerts = False` は、Projectが表示する警告や確認のダイアログを一時的に抑制する強力な機能です。自動化処理中にユーザーの介入を不要にするために使いますが、必ず処理後に `True` に戻すことを忘れてはいけません。これを怠ると、ユーザーがProjectを手動で操作する際に重要な警告を見逃すリスクがあります。これは、プログラムがユーザーの操作体験を奪うことのないよう配慮する、エンジニアの責任です。

4. `FileOpen` と `FileSaveAs` の「オブジェクトのライフサイクル」

ここが最も深い洞察を要する部分です。

  • `Set originalPrj = prjApp.FileOpen(…)` で、読み込み専用で開いたファイルへの参照を `originalPrj` 変数に格納します。
  • `prjApp.FileSaveAs(…)` を実行すると、新しいファイルがアクティブなプロジェクトになります。しかし、`originalPrj` が参照している元の読み込み専用ファイルは、Projectアプリケーション内部ではまだ開かれたままの状態です。
  • この状態では、Project内部に二つのプロジェクト(元の読み込み専用と、新しく保存された編集用)が開かれており、無駄なメモリを消費するだけでなく、ユーザーが誤って元のファイルに戻って作業を継続してしまうリスクがあります。
  • そこで、`originalPrj.Close` を呼び出すことで、元の読み込み専用ファイルを明示的に閉じます。これにより、リソースが解放され、Projectアプリケーションは新しい編集用ファイルのみをアクティブに保つことができます。これは、メモリフットプリントを最適化し、パフォーマンスを維持するための極めて重要なステップです。

5. `Set Nothing` によるオブジェクトの解放

使用し終わったオブジェクト変数を `Set Nothing` で解放することは、VBAプログラミングの鉄則です。これにより、オブジェクトが占有していたメモリが解放され、Projectアプリケーションの安定性とパフォーマンスが保たれます。特に Project のような重量級のアプリケーションでは、オブジェクトのライフサイクル管理がパフォーマンスに直結します。この習慣を身につけることが、一流の自動化エンジニアへの道です。

陥りやすいエラーとその対策

  • ファイルパスの指定間違い: `originalFilePath` や `newFullFilePath` に誤ったパスを設定すると、ファイルが見つからなかったり、意図しない場所に保存されたりします。今回のコードでは `Application.FileDialog` でユーザーに選択させることで、このリスクを大幅に低減しています。
  • ファイル名の重複: `FileSaveAs` で既存のファイル名と同じ名前を指定すると、上書き確認のダイアログが表示されます。今回はタイムスタンプを付加しているので、この問題は回避できます。
  • 権限エラー: ファイルの読み書き権限がないフォルダに保存しようとするとエラーになります。特に、ネットワークドライブやシステムフォルダへの保存には注意が必要です。
  • オブジェクトの解放忘れ: `Set Nothing` を忘れると、メモリリークやアプリケーションの不安定化に繋がることがあります。すべてのオブジェクトを使い終わったら必ず解放する癖をつけましょう。

さらに一歩踏み込む:発展的な知見

今回のコードをベースに、さらに堅牢でユーザーフレンドリーな自動化を構築するためのヒントです。

ログ機能の追加

マクロの実行状況やエラー情報をログファイルに記録することで、問題発生時の原因究明が容易になります。特に、複数のユーザーが利用する自動化では必須の機能です。

エラーハンドリングの強化

`On Error GoTo` を活用し、特定の行で発生したエラーをキャッチして、より詳細なメッセージをユーザーに提示したり、処理を安全に中断したりするロジックを追加できます。

ユーザーへのフィードバック

`MsgBox` だけでなく、Projectのステータスバーにメッセージを表示したり、進捗ダイアログを表示したりすることで、ユーザーはマクロが何をしているのかをより明確に理解できます。

まとめ:Project VBAの基本を掌握する一歩

お疲れ様でした! 今回は、Project VBAでファイルを「読み込み専用」で開き、堅牢な運用フローで「名前を付けて保存」する自動化について深く掘り下げてきました。

単にコードをコピペするだけでなく、その背後にある「データ保全の思想」「オブジェクトのライフサイクル」「パフォーマンスの重み」といった、伝説のチーフアーキテクトが日々考えている本質的な概念に触れていただけたなら幸いです。

今回の内容をしっかりと理解し、自分のものにできれば、Project VBAのファイル操作の基本はバッチリです。ここをクリアすれば、あなたは単なるマクロ記録の域を超え、真に業務を最適化できる自動化エンジニアへの道を力強く歩み始めることができるでしょう。

もし何か疑問があれば、いつでも尋ねてください。皆さんの業務自動化の旅を、これからも全力でサポートしていきます。

それでは、また次の記事でお会いしましょう!

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