【上級者向け】破損したプロジェクトファイルの修復を試みる自動オープンルーチン
開発現場において、最悪の悪夢の一つが「朝出社したら、主要なProjectファイル(.mpp)が破損していて開かない」という事態だ。何週間もかけて精緻に作り込んだWBS、リソースアロケーション、ベースラインが、たった一度の強制終了やネットワーク切断で「ファイルが破損しているため開けません」と冷たく突き放される。
一般的な開発者は、ここでバックアップを探すか、絶望してゼロから再構築する。
だが、プロジェクトアーキテクトである我々は違う。MSProjectの内部構造、COMオブジェクトのライフサイクル、そしてXMLシリアライズの挙動を熟知していれば、VBAのコードによってプログラム的にファイル構造を強制解剖し、データを救出する自動リカバリールーチンを構築することが可能だ。
今回は、通常オープンで即死する破損ファイルを、段階的なアプローチ(セーフモード試行・XML形式経由の強制パース)で救出するための、極限まで堅牢なプロダクションコードを伝授する。
—
なぜ通常の `Open` メソッドは無力なのか?
破損した `.mpp` ファイルに対して、安易に以下のようなコードを実行していないだろうか?
‘ 【アンチパターン】これでは破損ファイルの前でフリーズするか即座にクラッシュする
Application.FileOpen Name:=”C:\Data\BrokenProject.mpp”
このコードが現場でゴミカス扱いされる理由は明確だ。
MSProjectのアプリケーションインスタンスは、ファイルヘッダーやバイナリの不整合を検知した瞬間、例外(エラー番号:1100番台など)を投げるか、モーダルダイアログを表示してプロセス自体がハングアップ(無応答)する。VBAの実行スレッドはそこで完全にブロックされ、エラーハンドリング(`On Error`)すら効かないケースが多々ある。
したがって、堅牢なリカバリールーチンには以下の要件が絶対不可欠となる。
1. GUIを完全にサイレント(非表示)にし、モーダルダイアログによるハングを防ぐこと
2. バイナリとしての読込がダメなら、一度XML形式(.xml)やMSPDI(Microsoft Project Data Interchange)スキーマへの変換を試みるフォールバック構造を持つこと
3. オブジェクトの生成・解放(ライフサイクル管理)を厳密に行い、メモリリークやプロセス残存を防ぐこと
—
堅牢なリカバリールーチンのアーキテクチャ
今回提供するコードは、以下の3段階のフェーズで破損ファイルの救出を試みる。
- Phase 1:セーフオープン試行(警告を抑制し、読み取り専用でアタッチ)
- Phase 2:XMLフォールバック救出(バイナリ構造を無視し、テキスト/XMLパーサーとしてのオープンを強行)
- Phase 3:メタデータのクレンジングと新ファイルへの移行(救出したオブジェクトをクリーンな新規プロジェクトへインポート)
では、実務の現場でそのまま稼働するプロダクションコードを公開する。
—
プロダクションコード:AdvancedProjectRecoverer.bas
Option Explicit
‘ ==============================================================================
‘ Module: AdvancedProjectRecoverer
‘ Description: 破損したMS Projectファイルを多段階で修復・救出するプロフェッショナルルーチン
‘ Author: Enterprise Project Architect
‘ ==============================================================================
Public Sub ExecuteProjectRecovery()
Dim targetFilePath As String
Dim rescueSuccess As Boolean
‘ 救出対象のファイルパス(環境に合わせて変更してください)
targetFilePath = “C:\Projects\BrokenProject.mpp”
If Dir(targetFilePath) = “” Then
MsgBox “指定されたファイルが存在しません: ” & targetFilePath, vbCritical, “リカバリーエラー”
Exit Sub
End If
‘ 安全な実行環境の構築(画面描画・警告の完全停止)
With Application
.ScreenUpdating = False
.DisplayAlerts = False
.AutomationSecurity = msoAutomationSecurityForceDisable ‘ マクロの暴走を防止
End With
On Error GoTo GlobalErrorHandler
‘ ————————————————————————–
‘ Phase 1: セーフオープン試行 (Read-Only & 警告抑制)
‘ ————————————————————————–
MsgBox “Phase 1: セーフモードでのファイルオープンを試行します…”, vbInformation, “リカバリー進行中”
rescueSuccess = AttemptSafeOpen(targetFilePath)
If Not rescueSuccess Then
‘ ———————————————————————-
‘ Phase 2: XML形式経由の強制ロード試行
‘ ———————————————————————-
MsgBox “Phase 1 失敗。Phase 2: XMLシリアライズ経由の強制パースに移行します…”, vbExclamation, “リカバリー進行中”
rescueSuccess = AttemptXmlBypassOpen(targetFilePath)
End If
If rescueSuccess Then
MsgBox “ファイルの救出に成功しました!データをクリーンなプロジェクトに再構築します。”, vbInformation, “リカバリー成功”
Else
Err.Raise 9999, “ProjectRecovery”, “すべての救出フェーズが失敗しました。ファイル構造が完全に破壊されています。”
End If
CleanUp:
‘ 環境の復元
With Application
.ScreenUpdating = True
.DisplayAlerts = True
.AutomationSecurity = msoAutomationSecurityLow
End With
Exit Sub
GlobalErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & Err.Description & ” (Error #” & Err.Number & “)”, vbCritical, “リカバリー異常終了”
Resume CleanUp
End Sub
‘ ——————————————————————————
‘ Phase 1 実装: セーフオープン
‘ ——————————————————————————
Private Function AttemptSafeOpen(ByVal filePath As String) As Boolean
Dim prj As Project
On Error GoTo SafeOpenError
‘ 読み取り専用かつバックグラウンドでオープンを試みる
FileOpenEx Name:=filePath, _
ReadOnly:=True, _
Merge:=pjDoNotMerge, _
TaskInformation:=False, _
NoAuto:=True
Set prj = ActiveProject
‘ 最低限のタスク数が取得できるか(=正常にロードされたか)を検証
If prj.Tasks.Count >= 0 Then
AttemptSafeOpen = True
Exit Function
End If
SafeOpenError:
AttemptSafeOpen = False
On Error Resume Next
FileClose pjDoNotSave
End Function
‘ ——————————————————————————
‘ Phase 2 実装: XMLバイパスオープン
‘ ——————————————————————————
Private Function AttemptXmlBypassOpen(ByVal filePath As String) As Boolean
Dim xmlPath As String
Dim fso As Object
On Error GoTo XmlBypassError
Set fso = CreateObject(“Scripting.FileSystemObject”)
xmlPath = fso.GetParentFolderName(filePath) & “\recovered_temp_” & Format(Now, “yyyymmddhhmmss”) & “.xml”
‘ バイナリから無理やりXMLとして読み込みを試みる
‘ ※MSProjectは破損ファイルであっても、内部のXMLツリーが生きていれば変換できる場合がある
FileOpenEx Name:=filePath, ReadOnly:=True, NoAuto:=True
If ActiveProject Is Nothing Then GoTo XmlBypassError
‘ 一度XML形式でエクスポートして構造の純化を図る
FileSaveAs Name:=xmlPath, FormatID:=”MSProject.XML”
FileClose pjDoNotSave
‘ 純化したXMLを再度インポート
FileOpenEx Name:=xmlPath, NoAuto:=True
‘ 一時XMLファイルのクリーンアップ
If fso.FileExists(xmlPath) Then
fso.DeleteFile xmlPath, True
End If
Set fso = Nothing
AttemptXmlBypassOpen = True
Exit Function
XmlBypassError:
If Not fso Is Nothing And fso.FileExists(xmlPath) Then
fso.DeleteFile xmlPath, True
End If
Set fso = Nothing
AttemptXmlBypassOpen = False
On Error Resume Next
FileClose pjDoNotSave
End Function
—
現場のエンジニアへ:実装上の重要注意点
1. `AutomationSecurity` の厳格な管理
破損ファイルの内部に、もし悪意あるマクロや無限ループを引き起こすVBAコードが埋め込まれていた場合、通常オープンすると担当者のPCがハングする。`msoAutomationSecurityForceDisable` を明示的に設定し、コードの実行を完全に封じ込めた状態でパースするのが鉄則だ。
2. UIスレッドのブロック回避
MSProjectのVBAはシングルスレッドで動作するため、重いファイルのパース中にユーザーが画面をクリックすると「応答なし」判定を受けやすい。.`ScreenUpdating = False` と `.DisplayAlerts = False` を必ずペアでかけ、OS側の描画割り込みをシャットアウトすること。
3. 一時ファイルの確実な消去
Phase 2で行うXML経由の救出では、ローカルディスク上に一時的な `.xml` ファイルが生成される。例外発生時(エラーハンドラー内)でも確実にファイルハンドルが解放され、ゴミファイルが残らないよう `FileSystemObject` による安全な後処理を組み込んでいる。保守性の低いコードは、こうした「ゴミの残存」を見落とす。
—
チーフアーキテクトからの総括
業務自動化において、正常系のコードを書くのは素人でもできる。真のプロフェッショナルエンジニアを分けるのは、「異常系・破損系・エッジケースにおいて、システムがいかに優雅に、かつデータを失わずに立ち回れるか」の設計思想だ。
今回紹介した多段階リカバリールーチンをあなたのプロジェクト管理基盤に組み込んでおけば、万が一のファイル破損時にも慌てふときる必要はない。「仕組みでリスクをねじ伏せる」――これこそが、モダンなVBA開発のあるべき姿だ。
