Visioの「不可逆的破壊」を許さない:Document.SaveAsによる世代管理の極致
Visioのファイル形式(VSDX)は、実態はZIP圧縮されたXMLの集合体だ。複雑な図形構造やコネクタの論理関係を保持している以上、保存時のクラッシュは「単なるデータ消失」ではなく「数千時間分の工学資産の消滅」を意味する。
多くの開発者が標準の「自動バックアップ」機能に頼り、結果としてその脆弱性に泣く。真のアーキテクトであれば、アプリケーションのイベント層を掌握し、完全な制御下に置くべきだ。
本稿では、`Document.BeforeSave`イベントをフックし、`SaveAs`による世代管理を強制する、実務直結型のアーキテクチャを解説する。
—
1. オブジェクトライフサイクルの真実
VBAで最も多いミスは、イベントの接続状態を放置することによるメモリリークと、それによって引き起こされる「ゾンビプロセス」だ。`WithEvents`を適切に管理し、`Nothing`を代入して参照カウントを意図的に減らす。これが大規模システムを構築する上での鉄則である。
アーキテクチャの要諦
1. イベントの疎結合化: `ThisDocument`モジュールにロジックを詰め込むな。クラスモジュールにロジックを分離し、インスタンス管理を明確にする。
2. IOオーバーヘッドの最小化: `SaveAs`は重い処理だ。保存先がネットワークドライブの場合、同期処理を慎重に行う必要がある。
3. パスの完全制御: `Environ`や`FileSystemObject`を用いて、OSの制約を回避する絶対パスを生成する。
—
2. 実装:世代管理エンジン `BackupManager.cls`
このクラスモジュールをインポートし、プロジェクト内でインスタンス化せよ。
‘ クラスモジュール: BackupManager
Option Explicit
Private WithEvents vsoDoc As Visio.Document
Private Const BACKUP_DIR As String = “C:\VisioBackups\” ‘ 保存先パス
Public Sub Initialize(ByVal targetDoc As Visio.Document)
Set vsoDoc = targetDoc
End Sub
‘ ドキュメント保存直前にトリガーされるイベント
Private Sub vsoDoc_BeforeSave(ByVal doc As IVDocument)
Dim timestamp As String
Dim backupPath As String
Dim fso As Object
‘ タイムスタンプ生成 (ISO 8601準拠)
timestamp = Format(Now, “yyyyMMdd_HHmmss”)
‘ バックアップディレクトリの存在確認
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FolderExists(BACKUP_DIR) Then
fso.CreateFolder BACKUP_DIR
End If
‘ ファイル名の構築
backupPath = BACKUP_DIR & fso.GetBaseName(doc.FullName) & “_” & timestamp & “.vsdx”
‘ 本番保存を阻害しないよう、エラーハンドリングを厳重にする
On Error Resume Next
doc.SaveAsEx backupPath, VisSaveAsOptions.visSaveAsNoDefault
If Err.Number <> 0 Then
Debug.Print “バックアップ失敗: ” & Err.Description
End If
On Error GoTo 0
‘ オブジェクト解放
Set fso = Nothing
End Sub
Private Sub Class_Terminate()
‘ 循環参照防止のための明示的解放
Set vsoDoc = Nothing
End Sub
—
3. 呼び出し側の流儀 `ThisDocument`
`Document_Open`イベントでクラスを初期化し、アプリケーションのライフサイクルと同期させる。
‘ ThisDocumentモジュール
Option Explicit
Private mBackupManager As BackupManager
Private Sub Document_Open()
‘ インスタンスの生成と初期化
Set mBackupManager = New BackupManager
mBackupManager.Initialize ThisDocument
End Sub
Private Sub Document_BeforeClose(ByVal doc As IVDocument)
‘ 終了時には明示的にメモリを解放
Set mBackupManager = Nothing
End Sub
—
4. チーフアーキテクトからの助言:パフォーマンスと運用
非同期処理の代替案
もし図面サイズが数100MBを超える場合、`SaveAs`の同期実行はユーザー体験を著しく損なう。その場合は、`FileSystemObject.CopyFile`を用いて、保存済みファイルをバックグラウンドでコピーするロジックに切り替えるべきだ。APIレベルで非同期コピーを行うには `Kernel32.dll` の `CopyFileEx` を用いるのが定石だが、VBAで実装する場合は複雑性が増すため、まずは上記コードでボトルネックを計測することから始めてほしい。
なぜ `SaveAsEx` なのか
`SaveAs`ではなく`SaveAsEx`を使用している理由を問いたい。`visSaveAsNoDefault`オプションにより、Visioの「規定の動作」をバイパスし、上書き保存のダイアログや競合を完全に制御下に置くためだ。GUIの挙動に左右されない安定性を確保することが、自動化の生命線である。
最後に
コードを書くことは誰にでもできる。しかし、「いつ壊れても復旧できる」という絶対的な安心をシステムに組み込むことは、アーキテクトにしかできない。このバックアップロジックは、君が明日からの開発で、一度も「データ消失」を経験しないための最低限の防波堤となるだろう。
健闘を祈る。
