絶望の「ファイル破損」を乗り越える:Project VBAによる強制データ救出術
プロジェクトマネージャーにとって、数千行のタスクを積み上げた.mppファイルが「ファイルが破損しています」と冷酷に告げる瞬間は、死に等しい。通常のGUI操作では太刀打ちできない領域に踏み込むとき、我々エンジニアが頼れるのは、MS Projectのオブジェクトモデルの深層と、そこから抽出される「生データ」の力だ。
今日は、GUIを捨て、VBAの裏側から破損ファイルを「解剖」し、タスク情報を救出する極限のリカバリ技術を伝授する。
—
1. なぜ「通常オープン」は失敗するのか
Projectのファイルが壊れる原因の多くは、リソース定義の循環参照や、リンク情報の不整合、あるいはメモリ上のバイナリ・フラグの破損である。
GUIから開こうとすると、Projectの検証エンジンがエラーを検知し、処理を停止する。しかし、VBAの `Application.FileOpenEx` は、読み込みの制約を制御できるという特性がある。この「低レイヤーの読み込み制御」を使い、検証エンジンをバイパスしてデータに触れるのが救出の鍵だ。
—
2. 救出の鉄則:オブジェクトモデルの「直列読み込み」
救出において最も避けるべきは、破損したタスクの「計算エンジン」を走らせることだ。プロジェクトを開いた瞬間に再計算(Recalculate)が走ると、破損箇所でアプリがクラッシュする。
以下のコードは、「計算を停止」し、「読み取り専用」で開き、「タスクオブジェクトのプロパティを直接吸い出す」という、極めて低負荷なアプローチをとる。
実用プロダクションコード:タスク抽出ルーチン
‘ 警告: 本コードは極めて危険な操作を伴います。必ずバックアップをとってから実行してください。
Sub EmergencyDataRescue()
Dim projApp As Object
Dim targetFile As String
Dim sourceProj As Project
Dim destProj As Project
Dim t As Task
targetFile = “C:\Path\To\BrokenFile.mpp”
‘ 1. 計算エンジンを抑制し、最小限のメモリで開く
Application.Calculation = pjManual
On Error Resume Next
‘ ReadOnly:=True, OpenPool:=pjDoNotOpenPools で読み込み負荷を最小化
Set sourceProj = Application.Projects.Open(targetFile, , True, , , , , , , , , pjPoolReadOnly)
If sourceProj Is Nothing Then
MsgBox “ファイルの構造が深刻に破損しており、オブジェクトモデルすら生成できません。”, vbCritical
Exit Sub
End If
On Error GoTo 0
‘ 2. 新規プロジェクトを作成し、データを退避
Set destProj = Application.Projects.Add
‘ 3. 必要な属性をループで転記(※リンク関係は再計算リスクがあるため、まずは値のみ抽出)
For Each t In sourceProj.Tasks
If Not t Is Nothing Then
Dim newTask As Task
Set newTask = destProj.Tasks.Add(t.Name)
‘ 必要最小限のデータを転記(型変換の安全性を確保)
On Error Resume Next
newTask.Start = t.Start
newTask.Finish = t.Finish
newTask.Duration = t.Duration
newTask.PercentageComplete = t.PercentageComplete
On Error GoTo 0
End If
Next t
MsgBox “救出完了。新規プロジェクトへインポートしました。”
End Sub
—
3. なぜ「手作業」ではなく「コード」なのか
このコードの肝は、`Application.Calculation = pjManual` にある。
GUIで開くと、Projectは「破損したリンク」を修復しようと試み、そこで例外が発生する。しかし、VBAで `Task` オブジェクトを一つずつ叩く際は、Projectの計算エンジンを無視して「値そのもの」にアクセスできるため、リンク情報が壊れていてもタスク名や日付は回収できる可能性が高い。
プロフェッショナルが守るべき3つの注意点
1. リンク情報の復元を急がない: 破損ファイルからリンク(先行タスク等)を直接コピーしようとすると、参照先のタスクIDがずれており、二重のクラッシュを招く。まずは「期間」と「開始日」を確保し、リンクは後で再構築するのが鉄則だ。
2. `On Error Resume Next` の使い所: 破損箇所を全探索するため、エラーが出ることは前提だ。ループ内では必ずエラーを握りつぶし、ログとして「どの行が読めなかったか」を出力する設計にせよ。
3. 新規ファイルへの「値貼り付け」: `Paste` メソッドはクリップボードを介すため、メモリ汚染の原因になる。必ずオブジェクトのプロパティ(`Task.Start = SourceTask.Start`)を直接代入する方式をとること。
—
4. 次の一手:データベース連携のすすめ
もしこのレベルの破損が頻発するプロジェクトであれば、それは「ファイルという器」に限界が来ている証拠だ。
将来的には、タスクデータを `Recordset` でSQL ServerやAccessに退避する仕組みを構築すべきだ。バイナリファイルは「ブラックボックス」だが、データベースは「データの集合体」であり、今回のような救出劇すら不要になる。
「壊れないファイルを作る」のではない。「いつ壊れても、データだけは瞬時に復旧できるパイプラインを作る」。
これこそが、伝説的な自動化エンジニアの思考プロセスである。
君のプロジェクトが、このコードによって守られることを願う。健闘を祈る。
