Visio VBAを掌握する極限の知見:レガシーとモダンの狭間で溺れないための「ファイル形式完全判別・保存」アーキテクチャ
こんにちは。開発プロジェクトの現場において、数々の泥臭いファイルI/Oのトラブルを鎮圧してきたチーフアーキテクトの私だ。
Visio VBAの開発において、最も開発者を絶望の淵に追い込む瞬間の一つを知っているか?
それは、「ユーザーが勝手に古い `.vsd` 形式で保存してきた図面に対し、最新の `.vsdx` 前提のコードを走らせて実行時エラーを爆発させる」という、極めてプリミティブにして凶悪なバグだ。
組織内には、何年も前に作られた遺物の `.vsd` が平然と生き残っている。一方で、モダンな自動化スクリプトは `.vsdx`(XMLベースのZIP構造)を前提に組まれている。この「新旧の互換性の断絶」をコードレベルでシームレスに吸収し、環境やファイルの状態に依存しない堅牢な保存ルーチンを書くこと。これこそが、プロのVBAエンジニアと、コピペ職人の決定的な違いだ。
今回は、Visioのバージョン差異とファイル形式の罠を完全に見極め、「どんな古いファイルが持ち込まれても、安全に適切なフォーマットで保存・出力する」ためのプロダクションコードを授けよう。
—
なぜ、単純な `ActiveDocument.Save` では破綻するのか?
多くの初学者は、次のようなコードを書く。
‘ 【アンチパターン】これではレガシー環境や形式混在の現場で確実に死ぬ
ActiveDocument.Save
この書き方がプロの現場でタブー視される理由は明確だ。
1. ファイル形式のミスマッチ: `visSaveAs` メソッドにおいて、現在のドキュメントが `.vsd` なのか `.vsdx` なのかを意識せずに決め打ちで保存すると、Visioのエンジンが内部変換エラーを起こすか、意図しないフォーマットで上書きされる。
2. 新規未保存ドキュメント(Untitled)の罠: まだ一度も保存されていないドキュメントに対して `.Path` を取得しようとすると、VBAのランタイムエラー(エラー番号:1004など)が容赦なく発生する。
3. Visioのバージョンによる定数の差異: 古いVisio(2003〜2007等)と、モダンなVisio(2013以降)では、扱えるファイルフォーマット定数(`VisFileFormats`)のスコープが異なる。
これらをすべてロジカルに解決するのが、今回紹介する「FileFormatプロパティ監視型・動的保存アーキテクチャ」である。
—
堅牢なファイル保存・判定エンジンの設計思想
今回のコードのコアは、`Document.FileFormat` プロパティの正確な評価にある。Visioオブジェクトモデルにおいて、このプロパティは現在のファイルがどの形式でディスク上に存在するかを正確に返してくれる。
しかし、単に判定するだけでは不十分だ。以下の要件を満たす「実務で使える関数」に昇華させる必要がある。
- 未保存ドキュメントの検知: パスが空(`””`)の場合は、強制的に名前を付けて保存ダイアログ、あるいはデフォルトのパスへの新規保存へ誘導する。
- 拡張子の自動補正: ファイル名と内部の `FileFormat` が矛盾している場合(例: `.vsdx` という名前なのに中身が古い形式など)の暴走を防ぐ。
- エラーハンドリングの徹底: ロックファイルやアクセス権限の不備によるクラッシュを防ぐトランザクション的な記述。
—
【コピペ即実戦投入可】VSD/VSDX完全自動判別・保存モジュール
以下のコードを、あなたのVisioVBAプロジェクトの標準モジュール(例: `ModFileController.bas`)にそのまま貼り付けてほしい。実務の現場で耐えうるよう、厳格なエラーハンドリングと詳細なコメントを実装している。
Option Explicit
‘ ==============================================================================
‘ 模块名: ModFileController
‘ 用途: Visio図面のバージョン差異(VSD/VSDX)を吸収し、安全に保存・エクスポートする
‘ アーキテクト特権コード: 業務システム連携対応版
‘ ==============================================================================
‘ Visioファイル形式定数のフォールバック定義(古いバージョンへの配慮)
Const 早期バインディング用 = True
Public Sub SaveDocumentSafely(ByVal targetDoc As Visio.Document)
Dim currentPath As String
Dim currentName As String
Dim fileExtension As String
Dim saveFormat As VisFileFormats
On Error GoTo ErrorHandler
‘ 1. ドキュメントが有効かチェック
If targetDoc Is Nothing Then
MsgBox “対象となるドキュメントが存在しません。”, vbCritical, “保存エラー”
Exit Sub
End If
‘ 2. 新規未保存(パスが空)のドキュメントか判定
If targetDoc.Path = “” Then
Call SaveNewDocument(targetDoc)
Exit Sub
End If
currentPath = targetDoc.Path & targetDoc.Name
currentName = targetDoc.Name
fileExtension = LCase(Mid(currentName, InStrRev(currentName, “.”)))
‘ 3. FileFormatプロパティによる厳密なフォーマット判定
‘ Visio内部の保持形式と拡張子が一致しているかを検証する
Select Case targetDoc.FileFormat
Case visSaveAsXML, visSaveAsXMLDrawing, visSaveAsProtectedXMLDrawing
‘ VSDX形式(モダンXML)
saveFormat = targetDoc.FileFormat
If fileExtension <> “.vsdx” And fileExtension <> “.vsdm” Then
‘ 拡張子と内部形式の乖離がある場合の安全策
Debug.Print “警告: 内部形式はVSDXですが、拡張子が異なります。自動調整します。”
End If
Case visSaveAsDrawing, visSaveAsTemplate
‘ レガシーVSD形式(バイナリ)
saveFormat = visSaveAsDrawing
Case Else
‘ 万が一の未知の形式に対するフォールバック(デフォルトは現在の形式を維持)
saveFormat = targetDoc.FileFormat
End Select
друз
‘ 4. 実行保存処理
‘ ReadOnly(読み取り専用)の場合は上書き保存できないため別名保存を促す
If targetDoc.ReadOnly Then
MsgBox “このファイルは「読み取り専用」で開かれているため、上書き保存できません。” & vbCrLf & _
“別の名前で保存してください。”, vbExclamation, “保存警告”
Call SaveNewDocument(targetDoc)
Exit Sub
End If
‘ 安定した上書き保存の実行
targetDoc.Save
Debug.Print “正常保存完了: ” & currentPath & ” [Format: ” & targetDoc.FileFormat & “]”
Exit Sub
ErrorHandler:
Dim errDesc As String
errDesc = “予期せぬエラーが発生しました。” & vbCrLf & _
“Error No: ” & Err.Number & vbCrLf & _
“Description: ” & Err.Description
MsgBox errDesc, vbCritical, “SaveDocumentSafely – クリティカルエラー”
Debug.Print “[FATAL] ” & errDesc
End Sub
‘ ==============================================================================
‘ 補助ルーチン: 新規ドキュメント、または別名保存が必要な場合の処理
‘ ==============================================================================
Private Sub SaveNewDocument(ByRef targetDoc As Visio.Document)
Dim defaultFileName As String
Dim savedPath As Variant
‘ デフォルトのファイル名を生成(タイムスタンプ付与による一意性確保)
defaultFileName = “Drawing_” & Format(Now, “yyyymmdd_HHMMSS”) & “.vsdx”
‘ ユーザーに保存ダイアログを表示(Visio標準のUIを活用)
‘ ※完全自動化する場合は、ここで特定のフォルダパスを指定して .SaveAs を叩く
savedPath = Application.GetSaveAsFilename( _
InitialFileName:=defaultFileName, _
FileFilter:=”Visio 図面 (.vsdx), .vsdx, Visio 2003-2010 図面 (.vsd), .vsd”, _
Title:=”業務自動化ツール: 図面の保存先を指定してください” _
)
If savedPath = False Then
MsgBox “保存がキャンセルされました。”, vbInformation, “処理中断”
Exit Sub
End If
‘ 選択されたパスに基づいて保存を実行
‘ 拡張子を判定して適切な保存形式定数を動的に割り当てる
Dim ext As String
ext = LCase(Mid(savedPath, InStrRev(savedPath, “.”)))
If ext = “.vsd” Then
targetDoc.SaveAs CStr(savedPath) ‘ レガシー形式として保存
Else
targetDoc.SaveAs2 CStr(savedPath), visSaveAsXMLDrawing ‘ モダンVSDX形式として保存
End If
Debug.Print “新規・別名保存完了: ” & CStr(savedPath)
End Sub
—
現場のエンジニアへ:データベース・外部システム連携時の注意点
このコードをただコピペするだけではなく、エンタープライズ環境(SharePoint、Box、あるいは社内ファイルサーバーのDB管理下)で運用する際の「プロの知見」をいくつか共有しておこう。
1. ファイルロックの競合(SharePoint/OneDrive環境)
クラウドストレージ上の `.vsdx` をVBAから直接開いて高速保存を繰り返すと、同期競合(Sync Conflict)を引き起こすことがある。自動化バッチを回す際は、一度ローカル環境(`C:\Temp` など)にファイルをダウンロード(FileDownloader等で取得)し、処理が完結してからアップロードするパイプラインを組むのが鉄則だ。
2. トランザクション的思考を持て
一括処理で複数ファイルの形式変換(VSDからVSDXへの移行スクリプトなど)を行う場合は、`Application.Settings.AutoRecoverInterval` などの裏設定を一時的に無効化し、メモリリークや意図しないバックグラウンド保存を防ぐ配慮を忘れるな。
まとめ
Visioのバージョン差異やファイルフォーマットの壁は、正しいオブジェクトの知識と防御的プログラミング(Defensive Programming)によって完全に無力化できる。
「動けばいいや」という場当たり的なコードは、ある日突然、現場の業務を止める凶器に変わる。今回提供したアーキテクチャをあなたのツールに組み込み、一歩先の堅牢な自動化世界を構築してほしい。
健闘を祈る。
