【上級プロ向け】CorelDRAWバイナリ直撃!破損CDRファイルをVBAで強制サルベージする「自己治癒エンジン」の設計思想
開発現場で悪夢のような瞬間がある。何時間もかけて作り込んだ巨大なCorelDRAW(CDR)ファイルが、突然のフリーズやI/Oエラーによってクラッシュし、開けなくなったときだ。
「ファイルが破損しているため読み込めません」
この冷徹なエラーダイアログを前に、一般的なオペレーターは絶望する。しかし、我々自動化エンジニアにとって、ファイルフォーマットとは「ただのバイト列とテキスト」に過ぎない。
近代のCorelDRAW(バージョン11以降、特にX4/2019以降)のCDRファイルは、その実態がZIPコンテナであり、内部にXML(`content.xml`等)やバイナリリソースを隠し持ったオープンパッケージ構造である。
今回は、この構造をVBA(Visual Basic for Applications)から直接解体し、破損したドキュメントから生きたベクターパスデータやメタデータを強制的に救出する「自己治癒エンジン」の全貌を伝授する。
—
1. なぜ通常のCDRオープン処理はクラッシュに弱いのか?
CorelDRAWの標準オブジェクトモデル(`Application.OpenDocument`)は、ファイル構造にわずかでも不整合(CRCエラー、XMLのタグ不整合、RIFFヘッダの破損)があると、例外を吐いて処理をアボートする。オブジェクトモデルは「完全なドキュメント」を前提にメモリ上にツリーを構築するため、一部が欠損したファイルを修復する柔軟性を持たない。
したがって、CorelDRAWのGUIや標準APIを介さず、ファイルシステム・バイナリ・XMLパーサーのレイヤーで直接データをハッキングするアプローチが必要となる。
アーキテクチャの基本戦略
1. コンテナの強制解凍: CDRファイルをZIPとして扱い、VBA(またはWindows標準のCOMオブジェクト)で内部構造を一時ディレクトリに展開する。
2. XMLサニタイジング: 描画実態である`content.xml`の破損箇所(タグの未閉鎖など)を正規表現や文字列操作で強制修復する。
3. 新規ドキュメントへの再構築: 救出したXMLからパス座標やメタデータを読み込み、きれいな新規ドキュメント上にプログラム側でベクター要素を再描画する。
—
2. 堅牢なサルベージ・エンジンの実装
以下のコードは、破損したCDRファイルからZIPとして内部XMLを抽出し、VBAのDOM / XML解析技術を用いて安全領域へとデータをサルベージするプロダクションコードの骨子である。
Option Explicit
” ==========================================================================
” 圧倒的堅牢性を持つCDR自己治癒・サルベージエンジン
” Architecture: ZIP Container Extraction & XML Path Recovery
” ==========================================================================
Public Sub ExecuteEmergencySalvage(ByVal TargetCDRPath As String, ByVal OutputCDRPath As String)
Dim fso As Object
Dim tempDir As String
Dim extractedXmlPath As String
Set fso = CreateObject(“Scripting.FileSystemObject”)
tempDir = fso.GetSpecialFolder(2) & “\CDR_Salvage_” & Format(Now, “yyyymmddhhnnss”)
fso.CreateFolder tempDir
On Error GoTo ErrorHandler
‘ 1. CDRはZIPである:拡張子を偽装して強制解凍
Dim zipPath As String
zipPath = tempDir & “\target.zip”
fso.CopyFile TargetCDRPath, zipPath, True
Call UnzipFile(zipPath, tempDir)
‘ 2. 内部のコアデータ(content.xml等)の存在確認とサルベージ
extractedXmlPath = tempDir & “\content.xml”
If Not fso.FileExists(extractedXmlPath) Then
‘ バージョン違いによるパスの差異を吸収するフォールバック
extractedXmlPath = FindContentXml(tempDir, fso)
If extractedXmlPath = “” Then Err.Raise 9999, , “致命的エラー: 内部XMLストリームの特定に失敗しました。”
End If
‘ 3. 破損XMLのサニタイジング(タグの整合性確保)
Call SanitizeAndRepairXML(extractedXmlPath)
‘ 4. 新規ドキュメントを生成し、救出したデータからベクターを再構築
Dim doc As Document
Set doc = Application.CreateDocument()
Call RebuildVectorsFromXML(extractedXmlPath, doc)
‘ 5. 安全な状態で保存
doc.SaveAs OutputCDRPath
MsgBox “サルベージが完了しました。救出先: ” & OutputCDRPath, vbInformation, “自己治癒エンジン”
CleanUp:
On Error Resume Next
If fso.FolderExists(tempDir) Then fso.DeleteFolder tempDir, True
Set fso = Nothing
Exit Sub
ErrorHandler:
MsgBox “サルベージプロセスで異常終了しました: ” & Err.Description, vbCritical, “緊急停止”
Resume CleanUp
End Sub
Private Sub UnzipFile(ByVal zipFilePath As String, ByVal destFolderPath As String)
Dim sa As Object
Set sa = CreateObject(“Shell.Application”)
sa.NameSpace(destFolderPath).CopyHere sa.NameSpace(zipFilePath).Items, 4 ‘ 4 = 進行状況ダイアログ非表示
End Sub
Private Function FindContentXml(ByVal rootDir As String, ByVal fso As Object) As String
Dim folder As Object, subfolder As Object, file As Object
Set folder = fso.GetFolder(rootDir)
For Each file In folder.Files
If LCase(file.Name) = “content.xml” Or LCase(file.Name) = “document.xml” Then
FindContentXml = file.Path
Exit Function
End If
Next
‘ 再帰探索
For Each subfolder in folder.SubFolders
Dim res As String
res = FindContentXml(subfolder.Path, fso)
If res <> “” Then
FindContentXml = res
Exit Function
End If
Next
FindContentXml = “”
End Function
Private Sub SanitizeAndRepairXML(ByVal xmlPath As String)
‘ プロ仕様の応急処置:末尾の破損した閉じタグの補完や不正文字の除去
Dim fileNum As Integer
Dim fileContent As String
fileNum = FreeFile
Open xmlPath For Input As #fileNum
fileContent = Input$(LOF(fileNum), fileNum)
Close #fileNum
‘ 簡易的な自己治癒ロジック(例: 途中で切れたXMLの強制終端など)
If InStr(fileContent, ““) = 0 And InStr(fileContent, ““) = 0 Then
fileContent = fileContent & vbCrLf & “”
End If
fileNum = FreeFile
Open xmlPath For Output As #fileNum
Print #fileNum, fileContent
Close #fileNum
End Sub
Private Sub RebuildVectorsFromXML(ByVal xmlPath As String, ByRef targetDoc As Document)
‘ MSXML2.DOMDocument等を用いてXMLを解析し、CorelDRAWのオブジェクトモデルへマッピング
Dim xmlDoc As Object
Set xmlDoc = CreateObject(“MSXML2.DOMDocument.6.0”)
xmlDoc.Async = False
xmlDoc.Load xmlPath
If xmlDoc.ParseError.ErrorCode <> 0 Then
‘ パースエラーだが続行(ロバスト性の担保)
Debug.Print “XML Warning: ” & xmlDoc.ParseError.Reason
End If
‘ ここにXMLノードからCDRシェイプ(CreateCurve等)を再生成するロジックを実装
‘ 例: パスデータの座標配列を抽出してレイヤーに描画
Dim nodes As Object
Set nodes = xmlDoc.SelectNodes(“//path”) ‘ スキーマに応じたXPathを指定
Dim node As Object
For Each node In nodes
‘ 座標抽出とベクター再構築の処理…
Next node
End Sub
—
3. 実務運用における設計上の注意点(データベース連携とトランザクション)
こうしたサルベージツールを単発のスクリプトとして終わらせず、社内の生産管理システムやデジタルアセット管理(DAM)データベースと連携させる場合、以下のアーキテクチャ上の鉄則を守らなければならない。
1. 非破壊的処理(Non-Destructive Execution)
元の破損ファイルを直接上書きするような愚を犯してはならない。必ずタイムスタンプ付きの別名で出力し、データベース側には「破損検知→自動サルベージ成功」のステータスログをトランザクションとして記録する。
2. メモリリークの完全排除
VBAのCOMオブジェクト(`FileSystemObject`, `MSXML2.DOMDocument`等)は、ループ内やエラーハンドリング時において明示的に `Set xxx = Nothing` を行わないとメモリを肥大化させる。特に大量のCDRファイルをバッチ処理する現場では致命傷になる。
3. CorelDRAWのマルチインスタンス問題
バックグラウンドで自動修復を行う際、VisibleなCorelDRAWウィンドウを何度も立ち上げるとCOM通信がスタックする。可能な限りドキュメントの生成・操作は最小限のライフサイクルに抑え、処理終了後は速やかに解放すること。
—
チーフアーキテクトからの総括
「ファイルが壊れたから諦める」というのは、プロのエンジニアの選択肢には存在しない。CDRというフォーマットの正体を理解し、バイナリ・ZIP・XMLという階層構造を俯瞰できれば、VBAは単なるマクロ言語を超えた「強力なデータ蘇生ツール」へと変貌する。
定型業務の自動化にとどまらず、こうしたシステム障害のリカバリ領域にまでVBAの応用範囲を広げられたとき、あなたの自動化エンジニアとしての価値は、文字通り「次元の違う領域」に到達するはずだ。コードを磨き、どんな異常系をもねじ伏せる堅牢なシステムを構築してほしい。
