Visio VBAを掌握する極限の知見:Documentオブジェクト完全攻略
―― 複数図面を横断する一括処理エンジンの設計と実装
こんにちは。チーフアーキテクトの私だ。
これまで数多の巨大プラント図面、ネットワークトポロジ、そして複雑な業務フローチャートの自動化案件を手がけてきたが、現場からもっとも多く寄せられる悲鳴のような要望がこれだ。
「フォルダ内にある数百個のVisioファイルを開いて、特定のレイヤー設定を変更し、一括で上書き保存したい」
手作業でこれをやれば、何時間かかることか。いや、途中でVisioがメモリリークを起こしてフリーズするのがオチだ。
素人が書いたVBAコードは、決まって `Documents.Open` を乱発し、エラーハンドリングを怠り、最終的にゾンビプロセスと化した Visio.exe をタスクマネージャーに残して爆散する。
今回は、プロのエンジニアが現場で即座に採用できる、複数開いた(あるいはフォルダ内にある)図面ファイルを横断し、堅牢かつ高速に一括処理するDocumentオブジェクト完全攻略ノウハウを授けよう。
—
1. なぜ「雑なループ処理」は現場で破綻するのか?
多くの初学者は、次のようなコードを書く。
‘ 【アンチパターン】絶対に真似してはいけないコード
Sub BadExample()
Dim doc As Document
For Each doc In Documents
‘ プロパティを変更して保存
doc.PageSheet.CellsU(“PageWidth”).ResultIU = 297
doc.Save
Next doc
End Sub
何が問題か?
1. Documentsコレクションの動的変化を見落としている:処理の途中でドキュメントを開閉・追加すると、コレクションのインデックスがズレて強制終了(または無限ループ)を引き起こす。
2. ライフサイクルの無視:今どのドキュメントがアクティブなのか(`ActiveDocument`)に依存したコードを書くため、裏で何が起きているか制御不能になる。
3. エラー耐性の欠如:読み取り専用ファイルや、保護された図面に出くわした瞬間にマクロがクラッシュする。
我々が目指すべきは、「どのドキュメントがどの状態であっても、トランザクションを確実に完遂するエンジン」だ。
—
2. 実務を制する Documentオブジェクトの3大原則
Visioのオブジェクトモデルにおいて、`Document` は図面のデータ構造そのものであり、`Application.Documents` コレクションによって管理される。これを手懐けるための3箇条を叩き込む。
1. 配列への退避(Snapshot Pattern)
`Documents` コレクションを直接 `For Each` で回すな。処理対象のパスやオブジェクトの参照を一度配列(Array)やCollectionに退避(スナップショットを取得)してからループを回せ。これが動的変更によるバグを防ぐ鉄則だ。
2. 完全修飾名(Fully Qualified Names)の徹底
`ActivePage` や `ActiveShape` などのアクティブコンテキストに依存するな。`doc.Pages(1).Shapes(1)` のように、必ず `Document` 起点の階層構造で指定しろ。
3. 適切なメモリ解放とクローズ
マクロ内で開いたファイルは、処理終了後に明示的に閉じる(`Close`)。さもないと、Visioのインスタンスがメモリ上に残り続け、PCを再起動する羽目になる。
—
3. 【プロダクションコード】複数ファイル横断 一括プロパティ変更・保存エンジン
それでは、実務の現場でそのまま稼働できる、堅牢性を極めたVBAコードを公開する。
このスクリプトは、「指定したフォルダ内のすべての `.vsdx` ファイルをサイレント(非表示)で開き、全ページの用紙サイズを強制的にA4に変更し、メタデータを書き換えて上書き保存する」という、業務で頻出の要件を完璧に満たすものだ。
Option Explicit
‘ ==============================================================================
‘ 処理名 : BatchUpdateVisioDocuments
‘ 概要 : 指定フォルダ内の全Visioファイルを走査し、一括でプロパティ変更・保存を行う
‘ 著者 : Chief Architect
‘ ==============================================================================
Public Sub BatchUpdateVisioDocuments()
‘ 1. 定数定義
Const TARGET_FOLDER As String = “C:\Works\VisioFiles\” ‘ 対象フォルダのパス
Const TARGET_EXT As String = “.vsdx”
‘ 2. 変数宣言
Dim targetFiles() As String
String fileCount As Long
Dim filePath As Variant
Dim targetDoc As Document
Dim targetPage As Page
Dim fso As Object
Dim folder As Object
Dim file As Object
‘ 画面描画と警告を停止(パフォーマンス爆速化の基本)
Application.ScreenUpdating = False
Application.AlertsEnabled = False
On Error GoTo ErrorHandler
‘ 3. FileSystemObjectを用いた安全なファイル列挙
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FolderExists(TARGET_FOLDER) Then
MsgBox “指定されたフォルダが存在しません: ” & TARGET_FOLDER, vbCritical, “パスエラー”
GoTo Finally
End If
Set folder = fso.GetFolder(TARGET_FOLDER)
fileCount = 0
For Each file In folder.Files
If LCase(fso.GetExtensionName(file.Name)) = “vsdx” Then
ReDim Preserve targetFiles(fileCount)
targetFiles(fileCount) = file.Path
fileCount = fileCount + 1
End If
Next file
If fileCount = 0 {
MsgBox “対象となるVisioファイルが見つかりませんでした。”, vbInformation, “通知”
GoTo Finally
}
‘ 4. スナップショット配列に基づいた安全なドキュメント横断処理
For Each filePath In targetFiles
Debug.Print “処理中: ” & filePath
‘ 非表示(Invisible)でオープンし、UIのちらつきと無駄な描画コストを排除
Set targetDoc = Application.Documents.OpenEx(CStr(filePath), visOpenRONone + visOpenHidden)
‘ — 【ここから各ドキュメントに対する独自の一括処理を記述】 —
‘ 例1: すべてのページの用紙サイズを「A4 (縦)」に変更する
For Each targetPage In targetDoc.Pages
‘ ページシートのセルに対して安全にアクセス
targetPage.PageSheet.CellsU(“PageWidth”).ResultIU = 210 ‘ mm
targetPage.PageSheet.CellsU(“PageHeight”).ResultIU = 297 ‘ mm
targetPage.PageSheet.CellsU(“PrintLandscape”).ResultIU = 0 ‘ 0:縦, 1:横
Next targetPage
‘ 例2: ドキュメントプロパティ(コメントやタイトル)の付与
On Error Resume Next ‘ プロパティが存在しない場合の対策
targetDoc.DocumentSheet.CellsU(“Prop.Manager”).FormulaU = “””Automated by VBA Engine”””
On Error GoTo ErrorHandler
‘ — 【処理ここまで】 —
‘ 変更を保存して閉じる
targetDoc.Save
targetDoc.Close
Set targetDoc = Nothing
Next filePath
MsgBox “全 ” & fileCount & ” 件のファイルの処理が正常に完了しました。”, vbInformation, “完了”
Finally:
‘ 5. クリーンアップ処理(例外発生時も必ず通る)
Application.ScreenUpdating = True
Application.AlertsEnabled = True
Set fso = Nothing
Exit Sub
ErrorHandler:
‘ 6. 異常系ハンドリング
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “致命的エラー”
‘ 開きっぱなしのドキュメントがあれば強制クローズ(ゾンビ化防止)
If Not targetDoc Is Nothing Then
On Error Resume Next
targetDoc.Close visSaveNo
Set targetDoc = Nothing
On Error GoTo 0
End If
Resume Finally
End Sub
—
4. チーフアーキテクトが教える「実装の急所」
上記のコードには、現場で生き残るための高度なテクニックが凝縮されている。
① `Documents.OpenEx` と `visOpenHidden` のコンボ
大量のファイルを処理する際、ファイルを開くたびにVisioの編集画面がパチパチと切り替わる仕様では、描画オーバーヘッドだけで処理時間が10倍に跳ね上がる。
`visOpenHidden` フラグを立ててバックグラウンド(メモリ上のみ)でドキュメントを構築・操作することで、CPUとメモリの負荷を極限まで抑え込むことが可能だ。
② `ScreenUpdating` と `AlertsEnabled` の徹底的な制御
VBA自動化の鉄則だが、これを忘れると「上書き保存しますか?」といったダイアログでマクロが永久停止する。必ずプロシージャの冒頭で `False` にし、終了時(あるいはエラー時)の `Finally` ラベルで確実に `True` に戻す構造にしろ。
③ ゾンビプロセスの排除(エラー時の安全策)
処理途中でファイルが破損していたり、書き込み権限がない場合、エラーハンドラーにジャンプする。その際、`targetDoc` 変数が保持されたまま放置されるとメモリリークの原因になる。`If Not targetDoc Is Nothing Then` による厳格な二重チェックと強制クローズが、安定稼働の鍵を握る。
—
5. おわりに
Documentオブジェクトの制御は、Visio VBA自動化の「背骨」にあたる。ここが強固であれば、どれほど巨大な図面群であっても、あなたの右腕となって一寸の狂いもなく仕事を完遂してくれるはずだ。
「手作業で図面を開いて直す」という前時代的なルーティンワークからは、今日この瞬パッチで卒業したまえ。
君たちの健闘を祈る。
