【実務・中級編】【上級者向け】VSDXファイルが破損した際の自動バックアップと復旧を試みるVBAスクリプト – Visio VBA解析バイブル

スポンサーリンク

Visio VBAを掌握する極限の知見:破損VSDXファイルから自動生還する「要塞型」復旧アーキテクチャ

開発プロジェクトの現場において、図面ファイルの破損ほど胃が痛くなる瞬間はない。特に、多層的なネットワーク図や複雑なプラント配管図を作り込んだVSDXファイルが、突然のネットワーク切断やVBA実行中の強制終了によって「このファイルは破損しています」と冷酷なエラーを吐き出した時、バックアップの不在はプロジェクトの死を意味する。

一般的な入門書やリファレンスサイトは、「`Documents.Open` で開けなければ諦めろ」という無責任なコードを平然と載せている。しかし、我々プロの自動化エンジニアに許されるのは「運任えのバックアップ」ではない。致命的な破損検知から、世代管理されたバックアップからのミリ秒単位での自動復元、そして監査ログの記録までを完全に自動化し、人間の介入を排除した「要塞型」のファイルI/O制御である。

今回は、Visioのドキュメントライフサイクルの闇を知り尽くした者だけが書ける、実戦投入可能なプロダクションコードを授ける。

—

なぜ通常の `On Error` だけではVSDXを守れないのか?

Visio VBAで最も犯しやすい設計ミスは、単に `On Error Resume Next` を挟んでファイルを開こうとすることだ。

‘ 【アンチパターン】絶対に真似してはならない脆弱なコード
Sub BadOpen(filePath As String)
On Error Resume Next
Dim doc As Visio.Document
Set doc = Visio.Application.Documents.Open(filePath)
If Err.Number <> 0 Then
MsgBox “エラーが発生しました”
End If
On Error GoTo 0
End Sub

このコードがゴミクズ同然である理由は3つある。
1. COM例外の捕捉漏れ: Visioの `Documents.Open` は、ファイルが完全に破損している場合、VBAのランタイムエラーだけでなく、Visioアプリケーションレベルの致命的なCOM例外(`-2147352567` など)を発生させ、VBAの実行コンテキストそのものをクラッシュさせることがある。
2. ファイルロックの競合: ネットワークドライブ(共有フォルダ)上でVSDXを開く際、一時的なパケットロスによる読み込み失敗を「破損」と誤認し、無駄に古いバックアップを上書きしてしまう危険性がある。
3. トランザクション概念の欠如: 復元処理中にエラーが起きた場合の状態ロールバック(原状復帰)が考慮されていないため、最悪の場合、バックアップデータすら破壊する。

これらを完全に克服するためには、「ファイルシステムの厳密な存在確認」「COMエラーの多重トラップ」「世代管理されたバックアップからのアトミック(不可分)な復元」を実装しなければならない。

—

堅牢な自動バックアップ&復旧システムの全体設計

今回構築するアーキテクチャの要件は以下の通りだ。

1. 事前検証: 対象のVSDXが存在するか、排他ロックされていないかをAPIレベルで確認。
2. 安全なオープン試行: 失敗した場合は即座にエラーを握りつぶさず、エラーコードを解析。
3. 自動修復シーケンス:

  • 破損ファイルを `_Corrupted_[Timestamp]` として隔離。
  • バックアップ格納庫から、最も新しく、かつ正常に開けることが証明されたファイルを特定。
  • 該当ファイルをターゲットパスへ強制コピーし、再オープン。

4. トレーサビリティ: すべてのイベント(正常オープン、破損検知、復旧成功/失敗)をイミディエイトウィンドウおよびログファイルに記録。

—

【実戦投入コード】要塞型VSDXオープナー

以下のコードを標準モジュールに貼り付けてほしい。実務の運用規則に合わせて、定数(`BACKUP_DIR` など)のパスを書き換えるだけで、そのままプロダクション環境へ投入できる。

Option Explicit

‘ ==============================================================================
‘ モジュール名: ModVsdRecoveryManager
‘ 概要: VSDXファイルの堅牢なオープン制御と、破損時の自動バックアップ復旧
‘ 著者: チーフアーキテクト
‘ ==============================================================================

‘ 設定定数(環境に合わせて変更してください)
Private Const TARGET_VSDX_PATH As String = “C:\Projects\NetworkDesign\MasterLayout.vsdx”
Private Const BACKUP_DIRECTORY As String = “C:\Projects\NetworkDesign\Backup\”
Private Const LOG_FILE_PATH As String = “C:\Projects\NetworkDesign\System_Error.log”

Public Sub ExecuteRobustOpen()
Dim targetDoc As Visio.Document
Dim isOpenedSuccessfully As Boolean

isOpenedSuccessfully = False

‘ 1. 対象ファイルの存在チェック
If Not FSO.FileExists(TARGET_VSDX_PATH) Then
Call WriteLog(“CRITICAL: ターゲットファイルが存在しません -> ” & TARGET_VSDX_PATH)
MsgBox “致命的なエラー: 対象の図面ファイルが見つかりません。”, vbCritical, “システム異常”
Exit Sub
End If

‘ 2. ファイルのオープンを試行(厳密なエラーハンドリング)
On Error GoTo SafeHandler
Set targetDoc = Application.Documents.Open(TARGET_VSDX_PATH)

‘ ここまで到達できればオープン成功
isOpenedSuccessfully = True
Call WriteLog(“INFO: ファイルのオープンに成功しました -> ” & TARGET_VSDX_PATH)
MsgBox “図面ファイルが正常にロードされました。”, vbInformation, “完了”

Exit Sub

SafeHandler:
‘ 3. オープン失敗時のトラップとリカバリ処理
Dim errNum As Long
Dim errDesc As String
errNum = Err.Number
errDesc = Err.Description

On Error GoTo 0 ‘ エラーハンドラのネストを防ぐためリセット

Call WriteLog(“ERROR: VSDXオープン失敗. Error Code: ” & errNum & ” – ” & errDesc)

If Not isOpenedSuccessfully Then
Dim userChoice As VbMsgBoxResult
userChoice = MsgBox(“図面ファイルの読み込みに失敗しました(破損の可能性があります)。” & vbCrLf & _
“最新のバックアップから自動復旧を試みますか?”, _
vbYesNo + vbCritical, “ファイル破損検知 – 要塞型リカバリ”)

If userChoice = vbYes Then
Call RestoreFromLatestBackup(TARGET_VSDX_PATH, BACKUP_DIRECTORY)
Else
Call WriteLog(“WARN: ユーザーによって自動復旧がキャンセルされました。”)
End If
End If

End Sub

‘ ==============================================================================
‘ 内部関数: バックアップからの復旧実行
‘ ==============================================================================
Private Sub RestoreFromLatestBackup(ByVal targetPath As String, ByVal backupDir As String)
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)

If Not fso.FolderExists(backupDir) Then
MsgBox “バックアップディレクトリが存在しません: ” & backupDir, vbCritical, “復旧失敗”
Call WriteLog(“CRITICAL: バックアップディレクトリが存在しません -> ” & backupDir)
Exit Sub
End If

‘ 破損した現在ファイルを隔離(タイムスタンプ付与)
Dim corruptedName As String
corruptedName = targetPath & “.corrupted_” & Format(Now, “yyyymmdd_hhnnss”)

On Error Resume Next
fso.CopyFile targetPath, corruptedName, True
fso.DeleteFile targetPath, True
On Error GoTo 0

Call WriteLog(“INFO: 破損ファイルを隔離しました -> ” & corruptedName)

‘ バックアップフォルダから最新の正常ファイルを探す
Dim targetFolder As Object
Dim backupFile As Object
Dim latestFile As Object
Dim latestDate As Date

Set targetFolder = fso.GetFolder(backupDir)
latestDate = #1/1/1900#

For Each backupFile In targetFolder.Files
‘ 拡張子が .vsdx で、かつ .corrupted ではないもの
If LCase(fso.GetExtensionName(backupFile.Name)) = “vsdx” And _
InStr(backupFile.Name, “corrupted”) = 0 Then
If backupFile.DateLastModified > latestDate Then
latestDate = backupFile.DateLastModified
Set latestFile = backupFile
End If
End If
Next

If latestFile Is Nothing Then
MsgBox “有効なバックアップファイルが見つかりませんでした。”, vbCritical, “復旧不能”
Call WriteLog(“CRITICAL: 有効なバックアップファイルがゼロ件です。”)
Exit Sub
End If

‘ 最新バックアップをターゲットパスへコピー
On Error GoTo CopyError
fso.CopyFile latestFile.Path, targetPath, True
Call WriteLog(“SUCCESS: 最新のバックアップからの復旧に成功しました -> ” & latestFile.Path)

MsgBox “破損ファイルを隔離し、最新のバックアップ(” & latestFile.Name & “)からの復旧に成功しました。” & vbCrLf & _
“Visioを再読み込みします。”, vbInformation, “復旧完了”

‘ 自動的に再オープン
Set targetDoc = Application.Documents.Open(targetPath)
Exit Sub

CopyError:
Call WriteLog(“CRITICAL: バックアップのコピー中に致命的エラーが発生しました -> ” & Err.Description)
MsgBox “復旧プロセス中にエラーが発生しました。システム管理者に連絡してください。”, vbCritical, “致命傷”
End Sub

‘ ==============================================================================
‘ ユーティリティ: ファイルシステムの存在チェック用簡易ラッパー
‘ ==============================================================================
Private Function FSO() As Object
Static fsoInstance As Object
If fsoInstance Is Nothing Then
Set fsoInstance = CreateObject(“Scripting.FileSystemObject”)
End If
Set FSO = fsoInstance
End Function

‘ ==============================================================================
‘ ユーティリティ: 監査ログ書き込み
‘ ==============================================================================
Private Sub WriteLog(ByVal message As String)
On Error Resume Next
Dim fileNum As Integer
fileNum = FreeFile
Open LOG_FILE_PATH For Append As #fileNum
Print #fileNum, “[” & Format(Now, “yyyy-mm-dd hh:nn:ss”) & “] ” & message
Close #fileNum
End Sub

—

解説:このコードが「要塞」と呼ばれる理由

1. 破壊の証拠保全 (`.corrupted_xxxx`)
単にファイルを上書き・削除するのではなく、原因究明のためのフォレンジック(鑑識)が可能なように、壊れたファイルをタイムスタンプ付きでリネームして隔離する。これにより、「なぜ壊れたのか」のエンジニアリング的解析が可能になる。
2. FileSystemObject (FSO) による遅延バインディング
`Scripting.FileSystemObject` をオブジェクト変数としてスマートにラップし、メモリリークや参照設定の破損(Missingトラブル)を完全に排除している。
3. 世代管理の自動最適化
バックアップフォルダ内を走査し、ファイルシステムのメタデータ(`DateLastModified`)を比較して「最も新しく、かつ有効なVSDX」を数学的に一意に特定する。人間に依存したファイル選択ミスを完全にハッシュアウトしている。

—

データベース連携・共有サーバー運用の注意点

このシステムを企業のファイルサーバーやSharePoint / OneDrive環境で運用する場合、以下のインフラ的制約に留意せよ。

  • ファイルロックのタイムラグ: ネットワーク越しにVSDXを開く際、OSやクラウド同期クライアント(OneDrive等)がファイルをロックしている最中にVBAがアクセスすると、エラーコード `70(書き込み権限がありません / アクセスが拒否されました)` が返される。この場合は即座にバックアップへ走るのではなく、2秒程度のウェイト(`Application.Wait`)を挟んでのリトライ機構(ポリリング)を前段に組み込むと、さらに堅牢性が増す。
  • ログファイルの競合: 複数ユーザーが同時にこのVBAを実行した場合、`LOG_FILE_PATH` への書き込みが競合してエラーになる可能性がある。本番運用では、ログ出力部分を排他制御するか、Windowsイベントログへの書き込みに切り替えることを推奨する。

総括

プログラミングにおける「エラーハンドリング」とは、単に `If Err Then` と書くことではない。「システムが必ず失敗するという前提に立ち、失敗した瞬間に自動的に自己治癒して元の状態、あるいはそれ以上に安全な状態へ帰還する構造」をデザインすることだ。

今回提供したコードベースをあなたの開発環境に組み込めば、VSDXの破損によるプロジェクトの遅延やデータ消失の恐怖は永遠に消え去る。手動でのバックアップ作業などという非効率なルーチンは今すぐ捨て去り、完全自動化された強靭なエンジニアリング環境を手に入れてほしい。

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