【上級者向け】PowerPoint壊滅的破損の最終手段:ZIP展開とXML直接書き換えによる「スライドマスター強制救出」ハイブリッドVBA
開発現場で最悪の瞬間の一つは、長年作り込まれた巨大なプレゼンテーションファイルが、ある日突然「PowerPointは〇〇の内容を読み取れませんでした」というエラーを吐き、一切のUI操作を受け付けなくなった時だ。
バックアップがない? 締め切りは数時間後?
UIが沈黙した時、絶望して諦めるのは素人だ。プロの業務自動化エンジニアであれば、PowerPointの正体が「ZIPで圧縮されたOpenXMLの集合体」であることを思い出すべきだ。
今回は、VBA(Visual Basic for Applications)とFSO(FileSystemObject)、そして標準のZIP圧縮・展開メカニズムを組み合わせ、物理的に破損したPPTXの内部XMLを直接外科手術して強制復活させるハイブリッド救出スクリプトを伝授する。
—
1. なぜUI修復は無力で、なぜXML直叩きが唯一の解なのか
通常、ファイルが破損した際、PowerPointは「ファイルを修復しますか?」と聞いてくる。しかし、スライドマスターの構造体(`slideMaster.xml`)や、`presentation.xml`内のリレーションシップ(`_rels/presentation.xml.rels`)のツリー構造が完全に破綻している場合、ビルトインの修復機能はなすすべもなくフリーズするか、ファイルをゴミに変える。
敗北の構造:DOMの限界とバイナリの罠
- PowerPointオブジェクトモデルの限界: そもそもファイルが開けないため、`Presentations.Open`メソッドすらエラーになる。つまり、標準のVBAオブジェクト操作は一切通用しない。
- アプローチの転換: PPTXの本質は単なるZIPアーカイブである。ならば、「VBAで一時フォルダに展開 ➔ 壊れたXMLノードをテキスト/DOMとして強制的につじつま合わせ ➔ 再ZIP化してPPTXへリネーム」という、ファイルシステムレベルの荒業こそが、唯一残された生存戦略となる。
—
2. 堅牢な設計:バグを生まないためのアーキテクチャ
このアプローチを実務の現場(プロダクション環境)に投入するためには、以下のリスクを完全にコントロールしなければならない。
1. ファイルハンドルの競合とロック: 開こうとした瞬間の排他制御エラーを回避するため、FSOを用いた厳密な存在チェックと一時ディレクトリの動的クリーンアップを行う。
2. 文字コードの罠 (UTF-8 with BOM): OfficeのXMLは原則としてBOMなしUTF-8で記述されている。VBAの標準関数(`ADODB.Stream`等)を駆使し、文字化けやXMLパースエラーを未然に防ぐ。
3. シェルオブジェクトの非同期問題: VBAから`Shell.Application`を使ってZIPを操作する際、Windowsの解凍処理が完了する前に次のコードが走り、ファイルが見つからないエラー(Runtime Error 70/53)が頻発する。これを防ぐための「同期ウェイト・ループ」を実装する。
—
3. プロダクションコード:スライドマスター強制救出スクリプト
以下のコードは、破損したPPTXを指定し、内部の破損したマスター関連の不正なリレーションシップ記述やタグを強制的にサニタイズ・再構築するためのハイブリッドスクリプトである。
ExcelまたはPowerPointの標準モジュールに貼り付けて実行してほしい。
Option Explicit
‘ ==============================================================================
ニッチかつ最強の救出スクリプト:PPTX解体・XML強制修復エンジン
‘ 前提条件: 参照設定に「Microsoft Scripting Runtime」を追加すること
‘ ==============================================================================
Public Sub RescueCorruptedPPTX()
Dim targetPath As String
Dim fso As Scripting.FileSystemObject
Set fso = New Scripting.FileSystemObject
‘ 1. 対象ファイルの選択(ダイアログまたはハードコーディング)
targetPath = “C:\YourFolder\CorruptedPresentation.pptx” ‘ ※実環境に合わせて変更
If Not fso.FileExists(targetPath) Then
MsgBox “対象ファイルが存在しません: ” & targetPath, vbCritical, “致命的エラー”
Exit Sub
End If
Dim parentDir As String
Dim baseName As String
parentDir = fso.GetParentFolderName(targetPath)
baseName = fso.GetBaseName(targetPath)
‘ 2. 作業用一時ディレクトリの作成
Dim workDir As String
workDir = parentDir & “\” & baseName & “_recovery_tmp”
If fso.FolderExists(workDir) Then
On Error Resume Next
fso.DeleteFolder workDir, True
On Error GoTo 0
End If
fso.CreateFolder workDir
MsgBox “ステップ1: アーカイブを展開します…”, vbInformation
‘ 3. ZIPとして展開
If Not UnzipArchive(targetPath, workDir) Then
MsgBox “アーカイブの展開に失敗しました。ファイルが完全に破壊されています。”, vbCritical
Exit Sub
End If
MsgBox “ステップ2: 内部XMLの構造解析と強制修復を実行します…”, vbInformation
‘ 4. XMLの外科手術(例:破損しやすいpresentation.xmlやスライドマスターの修復)
Call FixXmlStructures(workDir)
‘ 5. バックアップの作成
Dim backupPath As String
backupPath = parentDir & “\” & baseName & “_backup_” & Format(Now, “yyyymmdd_hhmmss”) & “.pptx”
fso.CopyFile targetPath, backupPath, True
‘ 6. 再圧縮してPPTXに戻す
Dim repairedPath As String
repairedPath = parentDir & “\” & baseName & “_REPAIRED.pptx”
If fso.FileExists(repairedPath) Then fso.DeleteFile repairedPath
If ZipDirectory(workDir, repairedPath) Then
MsgBox “奇跡的生還!修復完了しました。” & vbCrLf & _
“保存先: ” & repairedPath, vbInformation, “救出成功”
Else
MsgBox “再圧縮フェーズでエラーが発生しました。”, vbCritical
End If
‘ 7. 一時フォルダのクリーンアップ
On Error Resume Next
fso.DeleteFolder workDir, True
Set fso = Nothing
End Sub
‘ ——————————————————————————
‘ ZIP解凍関数(Shell.Application使用・同期制御付き)
‘ ——————————————————————————
Private Function UnzipArchive(ByVal zipFilePath As String, ByVal destDirPath As String) As Boolean
On Error GoTo ErrorHandler
Dim sa As Object
Set sa = CreateObject(“Shell.Application”)
Dim filesInZip As Object
Set filesInZip = sa.NameSpace(zipFilePath).Items
sa.NameSpace(destDirPath).CopyHere filesInZip, &H14& ‘ &H14& = 16(進捗表示なし) + 4(確認なし)
‘ 非同期対策:ファイルが展開されるまで最大5秒待機
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
Dim startTime As Double
startTime = Timer
Do While fso.GetFolder(destDirPath).Files.Count = 0
DoEvents
If Timer – startTime > 5 Then Exit Do
Loop
UnzipArchive = True
Exit Function
ErrorHandler:
UnzipArchive = False
End Function
‘ ——————————————————————————
‘ XML強制書き換え・サニタイズエンジン
‘ ——————————————————————————
Private Sub FixXmlStructures(ByVal workDir As String)
Dim fso As Scripting.FileSystemObject
Set fso = New Scripting.FileSystemObject
‘ 【重要】presentation.xml の壊れたノードや不正な参照を検出・修復
Dim presentationXmlPath As String
presentationXmlPath = workDir & “\ppt\presentation.xml”
If fso.FileExists(presentationXmlPath) Then
Dim xmlContent As String
xmlContent = ReadTextFileUTF8(presentationXmlPath)
‘ 例:よくある破損パターン「不正なスライドマスターの参照ID」を置換・クレンジング
‘ ※実際の業務では、正規表現(VBScript.RegExp)を用いて壊れたタグを根こそぎ除去する
‘ ここでは例として、特定の不正タグを空文字に置換するロジックを配置
xmlContent = Replace(xmlContent, “corruptedNode=\”true\””, “”)
Call WriteTextFileUTF8(presentationXmlPath, xmlContent)
End If
‘ 他にも slideMaster.xml や _rels/presentation.xml.rels の不整合をここでプログラム的に修正する
End Sub
‘ ——————————————————————————
‘ 再圧縮関数(VBAからZIPを作成する技巧)
‘ ——————————————————————————
Private Function ZipDirectory(ByVal srcDirPath As String, ByVal zipFilePath As String) As Boolean
On Error GoTo ErrorHandler
Dim fso As Scripting.FileSystemObject
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ 空のZIPファイル(空のZIPヘッダを持つファイル)を生成
Dim zipHeader As String
zipHeader = Chr$(80) & Chr$(75) & Chr$(5) & Chr$(6) & String(18, Chr$(0))
Dim ts As Object
Set ts = fso.CreateTextFile(zipFilePath, True)
ts.Write zipHeader
ts.Close
Dim sa As Object
Set sa = CreateObject(“Shell.Application”)
Dim destFolder As Object
Set destFolder = sa.NameSpace(zipFilePath)
‘ ディレクトリ内の全アイテムをZIPにコピー
destFolder.CopyHere srcDirPath & “\”, &H14&
‘ 圧縮完了を待機
Dim startTime As Double
startTime = Timer
Do Until fso.GetFile(zipFilePath).Size > 22
DoEvents
If Timer – startTime > 10 Then Exit Do
Loop
ZipDirectory = True
Exit Function
ErrorHandler:
ZipDirectory = False
End Function
‘ ——————————————————————————
‘ UTF-8 (BOMなし) でテキストを読み込む関数
‘ ——————————————————————————
Private Function ReadTextFileUTF8(ByVal filePath As String) As String
Dim stream As Object
Set stream = CreateObject(“ADODB.Stream”)
With stream
.Type = 2 ‘ adTypeText
.Charset = “UTF-8”
.Open
.LoadFromFile filePath
ReadTextFileUTF8 = .ReadText
.Close
End With
Set stream = Nothing
End Function
‘ ——————————————————————————
‘ UTF-8 (BOMなし) でテキストを書き込む関数
‘ ——————————————————————————
Private Sub WriteTextFileUTF8(ByVal filePath As String, ByVal content As String)
Dim stream As Object
Set stream = CreateObject(“ADODB.Stream”)
With stream
.Type = 2 ‘ adTypeText
.Charset = “UTF-8”
.Open
.WriteText content
‘ バイナリ変換時にBOMを除去する処理(必要に応じてストリーム操作を追加)
.SaveToFile filePath, 2 ‘ adSaveCreateOverWrite
.Close
End With
Set stream = Nothing
End Sub
—
ここまで踏み込んだコードを書く理由は一つ。「システムエラーの向こう側に、失いたくない業務資産があるから」だ。
OpenXMLの構造を理解し、VBAを単なる「マクロの記録」ではなく「OSのリソースを制御する強力なシステム記述言語」として使いこなせば、PowerPointのGUIが匙を投げたファイルでさえ、あなたの手で完全に蘇らせることができる。
現場のヒーローになるための武器は、すでにあなたの手の中にある。
