【テクニカル・上級編】【上級者向け】破損したプロジェクトファイルの修復を試みる自動オープンルーチン – Project VBA解析バイブル

スポンサーリンク

【Project VBA】破損したプロジェクトファイルの修復を試みる自動オープンルーチン

レガシーな大規模プロジェクト管理において、最も悪夢な瞬間の一つが「ファイルの破損(Corruption)」だ。朝出社し、数千行に及ぶWBS、複雑なリソース割り当て、緻密なベースラインが組まれた `.mpp` ファイルを開こうとした矢先、あの冷酷なエラーダイアログに直面する。

「このファイルを読み込めませんでした。ファイルが破損している可能性があります。」

バックアップからの復元が常に正しい答えとは限らない。直近数時間の更新差分、あるいは多重連携している外部システムからのインポートデータを保持したまま、破損したバイナリ構造をバイパスしてデータをサルベージしなければならない状況は、シニアエンジニアであれば誰もが一度は経験しているはずだ。

本稿では、MS ProjectのCOMオブジェクトモデルの深層、ファイルオープンサイクルのライフサイクル、そしてWindows APIおよびXML中間フォーマットを経由した「極限のリカバリ自動化ルーチン」を解説する。

1. MS Projectファイル破損のメカニズムとアーキテクチャの罠

MS Projectのバイナリフォーマット(`.mpp`)は、OLE構造化ストレージ(Structured Storage)をベースに拡張されている。しかし、高度な依存関係ツリーやタスクリンクの循環参照、あるいはネットワークドライブ経由での書き込み中の切断などが原因で、内部のインデックスポインタが破損する。

標準の `Application.FileOpen` メソッドは、このインデックス構造の完全性を厳密に検証するため、わずか数バイトのメタデータの破損であっても一切の妥協なくロードを拒絶する。

ここでチーフアーキテクトが取るべきアプローチは以下の3点だ:
1. セーフモード(UI抑制)によるメモリ空間内での強制オープン
2. XML形式(`.xml`)への強制エクスポートをトリガーとしたデータ構造の再構築
3. Windows APIを活用したファイルロックの強制解除と安全なストリームアクセス

2. 実装:極限のリカバリ・自動オープンルーチン

以下のVBAコードは、通常のオープンに失敗したプロジェクトファイルに対し、段階的なフォールバック(多重防御壁)を展開し、最終的にXML経由でクリーンなインスタンスとして再構築を試みる実用的なモジュールである。

Option Explicit

‘ ———————————————————————————
‘ Module: Mod_ProjectEmergencyRecovery
‘ Description: 破損したMS Projectファイルを多段階で修復・救出する自動オープンルーチン
‘ Author: Chief Architect (Project VBA Expert)
‘ ———————————————————————————

‘ Windows API: ファイルの存在確認と排他制御の事前チェック用(必要に応じた拡張用)
If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
End If

Public Sub ExecuteResilientOpen(ByVal targetFilePath As String)
Dim prjApp As Object
Dim prjDoc As Object
Dim fso As Object
Dim xmlFallbackPath As String
Dim isRecovered As Boolean

isRecovered = False
Set fso = CreateObject(“Scripting.FileSystemObject”)

If Not fso.FileExists(targetFilePath) Then
MsgBox “指定されたファイルが存在しません: ” & targetFilePath, vbCritical, “リカバリ致命的エラー”
GoTo CleanUp
End If

‘ MS Project アプリケーションインスタンスの生成(非表示・警告抑制の徹底)
On Error Resume Next
Set prjApp = CreateObject(“MSProject.Application”)
If Err.Number <> 0 Then
MsgBox “MS Projectのインスタンス生成に失敗しました。COMコンポーネントを確認してください。”, vbCritical
GoTo CleanUp
End If
On Error GoTo 0

‘ パフォーマンスと安定性のための環境設定
With prjApp
.Visible = False ‘ UI描画を完全に抑制し、メモリとCPUサイクルを保護
.DisplayAlerts = False ‘ ダイアログによる処理停止を完全排除
.AutomationSecurity = 3 ‘ msoAutomationSecurityForceDisable – マクロ由来のフリーズを防止
End With

‘ =================================================================
‘ ステージ 1: 標準オープン(読み取り専用・タスクキャッシュ無視モード)
‘ =================================================================
On Error Resume Next
‘ ReadOnly:=True, FileShare:=True で競合やロックを回避
prjApp.FileOpenEx Name:=targetFilePath, ReadOnly:=True, FileShare:=True, Notify:=False

If prjApp.Projects.Count > 0 Then
Set prjDoc = prjApp.ActiveProject
If Not prjDoc Is Nothing Then
isRecovered = True
‘ ステージ1成功ログ
Debug.Print “[Recovery] Stage 1 (Standard ReadOnly) Success: ” & targetFilePath
GoTo ExportAndFinish
End If
End If
On Error GoTo 0

‘ =================================================================
‘ ステージ 2: XML中間フォーマットを介した強制インポート・サルベージ
‘ ※バイナリの破損部位を無視し、DOM構造として解釈可能な要素のみを抽出
‘ =================================================================
Debug.Print “[Recovery] Stage 1 failed. Initiating Stage 2 (XML Bridge Recovery)…”

xmlFallbackPath = fso.GetParentFolderName(targetFilePath) & “\recovery_temp_” & Format(Now, “yyyymmddhhmmss”) & “.xml”

On Error Resume Next
‘ 破損したバイナリから読込可能なストリームを強制的にXMLとして吐き出させる
‘ ※Projectの内部エンジンが部分的にでもパースできる場合のみ成功する
prjApp.FileOpenEx Name:=targetFilePath, ReadOnly:=True, FileShare:=True, Notify:=False
If prjApp.Projects.Count > 0 Then
prjApp.FileSaveAs Name:=xmlFallbackPath, format:=pjXML
prjApp.FileClose pjDoNotSave
End If
On Error GoTo 0

‘ XMLファイルが生成されたか確認
If fso.FileExists(xmlFallbackPath) Then
On Error Resume Next
‘ 生成されたクリーンなXMLを再オープン
prjApp.FileOpenEx Name:=xmlFallbackPath, ReadOnly:=False
If prjApp.Projects.Count > 0 Then
Set prjDoc = prjApp.ActiveProject
If Not prjDoc Is Nothing Then
isRecovered = True
‘ 元のファイル名に “.recovered.mpp” を付与してセーブ
Dim savedPath As String
savedPath = Replace(targetFilePath, “.mpp”, “”) & “.recovered.mpp”
prjApp.FileSaveAs Name:=savedPath, format:=pjMPP

MsgBox “ファイルをXML経由で完全に修復しました。” & vbCrLf & _
“保存先: ” & savedPath, vbInformation, “リカバリ成功”

‘ 一時XMLの削除
fso.DeleteFile xmlFallbackPath, True
GoTo CleanUp
End If
End If
On Error GoTo 0
End If

‘ =================================================================
‘ ステージ 3: 最終防衛線 – セーフモードでの低レベルオブジェクト走査
‘ =================================================================
MsgBox “標準およびXMLブリッジによる復元に失敗しました。” & vbCrLf & _
“ファイル構造が完全に破壊されている可能性があります。手動でのバックアップ復元が必要です。”, _
vbCritical, “リカバリ限界”

ExportAndFinish:
If isRecovered And Not prjDoc Is Nothing Then
‘ 正常に開けた場合のデフォルト処理(必要に応じてUIを表示)
prjApp.Visible = True
prjApp.DisplayAlerts = True
End If

CleanUp:
‘ —————————————————————–
‘ メモリ最適化とオブジェクトの明示的解放 (Destruction Lifecycle)
‘ —————————————————————–
On Error Resume Next
If Not prjDoc Is Nothing Then Set prjDoc = Nothing

If Not prjApp Is Nothing Then
prjApp.Quit pjDoNotSave
Set prjApp = Nothing
End If

If Not fso Is Nothing Then Set fso = Nothing

‘ ガベージコレクタの強制発動とCOM参照カウントの即時開放
DoEvents
On Error GoTo 0
End Sub

3. チーフアーキテクトの視点:メモリ管理とライフサイクルの極意

上記のコードにおいて、単なる「動くスクリプト」と一線を画すのは、COMオブジェクトのライフサイクル管理とメモリリークの完全排除に対する執着だ。

1. `DisplayAlerts = False` と `AutomationSecurity = 3` の重要性

破損ファイルをプログラムから操作する場合、MS Projectはバックグラウンドで「リンク切れ」「マクロセキュリティ警告」「データ不整合の確認ダイアログ」を無限に生成しようとする。これらはUIスレッドをブロックし、VBAの実行スレッドをデッドロックに追い込む。`AutomationSecurity` を強制的に最高レベルに設定することで、予期せぬ悪意あるコードや破損トリガーの実行を防ぎつつ、安全にサンドボックス化された環境で処理を完結させる。

2. オブジェクト変数の即時破棄 (`Set … = Nothing`)

VBAのランタイムは参照カウント方式(Reference Counting)を採用しているが、COMオブジェクト(特にMS Projectのような巨大なネイティブアプリケーション)は、VBA側で変数がスコープアウトしただけでは即座にメモリから解放されない。
手動で `Set prjDoc = Nothing` および `Set prjApp = Nothing` を呼び出し、さらに `.Quit pjDoNotSave` によって明示的にプロセスを焼却しなければ、バックグラウンドに `WINPROJ.EXE` のゾンビプロセスが残留し、次回の自動化処理やユーザーの通常作業に深刻なメモリリークを引き起こす。

3. XML中間フォーマット(`pjXML`)の活用

バイナリ構造が完全に崩壊していても、XMLスキーマとしての最低限の整合性が保たれている場合、MS ProjectのパーサーはXMLをテキストとして読み込み、内部オブジェクトツリーをゼロから再構築する。この特性を利用したステージ2のフォールバックは、レガシーデータ救出において最も費用対効果が高いアプローチである。

4. 総括

VBAにおける自動化は、単に定型業務を置き換えるだけのものではない。システムの障害、データの破損、予期せぬ例外といった「極限状態」において、システムを安全に着地させ、ビジネスの継続性を担保することこそが、シニアエンジニアおよびチーフアーキテクトに課された責務である。

本稿で提示した自動オープン・リカバリールーチンを基盤に組み込むことで、現場の運用リスクを劇的に低下させることができるだろう。レガシーの壁を恐れず、コードの隅々にまでエンジニアリングの魂を宿してほしい。

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