Visio VBAを掌握する極限の知見:完全サイレントなバッチ処理の構築
大規模な図面群のバッチ処理において、最大のボトルネックは「VBAのコード実行速度」ではない。Visioが親切心から裏で必死に実行している「画面描画」「UIイベントのディスパッチ」「ドキュメント変更通知の伝播」という名の無駄なオーバーヘッドである。
何百枚もの `.vsd` / `.vsdx` ファイルを連続処理する際、これらを野放しにしていれば、タスクマネージャーのCPU使用率は低空飛行のまま、ひたすらI/Oと描画待ちで数時間の拘束を強いられることになる。
本稿では、Visio VBAの根幹を成すオブジェクトモデルとアプリケーション制御を極限までチューニングし、完全サイレントかつ爆速で動作するバッチ処理アーキテクチャの全貌を解説する。
—
1. Visioサイレント化の3大柱
Visioを真のヘッドレス(バックグラウンド)状態で駆動させるためには、単に画面を隠すだけでは不十分だ。以下の3つの防壁を完全に構築する必要がある。
1. `Application.ScreenUpdating` の遮断:DOM変更に伴う画面の再描画を完全に停止する。
2. `Application.ShowChanges` の無効化:図面変更時の内部通知・プレビュー更新を断ち切る。
3. `Application.AlertsEnabled` の抑制:予期せぬダイアログによる処理の停止を物理的に封殺する。
これらを適切に組み合わせることで、Visioは「GUIアプリケーション」から「超高効率な図形演算エンジン」へと変貌を遂げる。
—
2. 実装コード:限界突破バッチ処理エンジン
以下に、実業務でそのまま耐えうる、エラーハンドリングとメモリ管理を極限まで高めたバッチ処理のテンプレートを示す。
Option Explicit
‘ =========================================================================
‘ 伝説のチーフアーキテクトによる完全サイレント・バッチ処理テンプレート
‘ =========================================================================
Sub ExecuteExtremeBatchProcess()
Dim startTime As Double
startTime = Timer
‘ 1. アプリケーションレベルの安全な環境退避
Dim origScreenUpdating As Boolean
Dim origShowChanges As Boolean
Dim origAlertsEnabled As Boolean
Dim origDeferRecalc As Boolean
On Error GoTo ErrorHandler
With Application
origScreenUpdating = .ScreenUpdating
origShowChanges = .ShowChanges
origAlertsEnabled = .AlertsEnabled
origDeferRecalc = .DeferRecalc
‘ — 極限の高速化設定(完全サイレント化) —
.ScreenUpdating = False ‘ 描画の完全停止
.ShowChanges = False ‘ 変更通知の遮断(これが最大の肝)
.AlertsEnabled = False ‘ 警告ダイアログの抑制
.DeferRecalc = True ‘ 数式・シェイプシートの再計算を遅延
End With
‘ 2. 処理対象ファイルのリストアップ(ここでは簡略化のためハードコードまたはループ想定)
Dim targetFiles As Collection
Set targetFiles = GetTargetFilesList(“C:\VisioWork\Input\”)
Dim filePath As Variant
Dim targetDoc As Document
Dim processedCount As Long
processedCount = 0
For Each filePath in targetFiles
‘ 3. Invisibleモードでのドキュメントオープン
‘ Documents.OpenEx を使用し、余計なウィンドウを一切生成しない
Set targetDoc = Application.Documents.OpenEx(CStr(filePath), visOpenRO + visOpenHidden)
‘ — 4. 実際の重い処理をここで実行 —
Call ProcessSingleDocument(targetDoc)
‘ 5. 変更を保存して閉じる(UIを介さない完全サイレント保存)
targetDoc.Saved = False ‘ 強制的に変更有りとマーク(必要に応じて)
targetDoc.SaveAs “C:\VisioWork\Output\” & Mid(filePath, InStrRev(filePath, “\”) + 1)
targetDoc.Close
‘ 6. 循環参照やメモリ肥大化を防ぐためのオブジェクト解放
Set targetDoc = Nothing
processedCount = processedCount + 1
Next filePath
CleanUp:
‘ 7. 状態の確実な復元(エラー発生時も必ず実行)
With Application
.ScreenUpdating = origScreenUpdating
.ShowChanges = origShowChanges
.AlertsEnabled = origAlertsEnabled
.DeferRecalc = origDeferRecalc
End With
MsgBox “バッチ処理完了: ” & processedCount & ” ファイル処理 (” & Format(Timer – startTime, “0.00秒”) & “)”, vbInformation
Exit Sub
ErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & 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 CleanUp
End Sub
‘ 実際の図形操作ロジック(サンプル)
Private Sub ProcessSingleDocument(ByRef doc As Document)
Dim pg As Page
Dim shp As Shape
‘ DeferRecalc = True の恩恵を受け、ここではシェイプシートの再計算は走らない
For Each pg in doc.Pages
For Each shp in pg.Shapes
‘ 例: 特定のプロパティ書き換え
‘ shp.CellsU(“Prop.Status”).FormulaU = “””Processed”””
Next shp
Next pg
End Sub
‘ 対象ファイル取得ヘルパー
Private Function GetTargetFilesList(ByVal folderPath As String) As Collection
Dim col As New Collection
Dim fileName As String
If Right(folderPath, 1) <> “\” Then folderPath = folderPath & “\”
fileName = Dir(folderPath & “.vsd”)
Do While fileName <> “”
col.Add folderPath & fileName
fileName = Dir
Loop
Set GetTargetFilesList = col
End Function
—
3. チーフアーキテクトが解説する「深層の知見」
① `ShowChanges = False` の真の挙動
多くの開発者は `ScreenUpdating = False` だけを設定して満足する。しかし、Visioアーキテクチャにおいて、DOM(Document Object Model)の変更は親ウィンドウや外部のアドイン、さらにはUndoスタックに対して「変更イベント」をブロードキャストする。
`ShowChanges = False` は、この内部イベントのディスパッチそのものをバイパスする。これにより、メモリ消費量の爆発的な増加と、CPUのコンテキストスイッチを劇的に抑制できる。
② `DeferRecalc = True` との組み合わせによる相乗効果
Visioのシェイプシート(ShapeSheet)は、1つのセルが変更されると、それに依存するすべての数式を再計算(Cascade Calculation)する。バッチ処理で何千個ものシェイプを連続して書き換える場合、この再計算が毎回走ると処理が完全に破綻する。
`DeferRecalc = True` を宣言することで、再計算のキューをメモリ上に保留し、処理終了時(あるいはフラグを戻した瞬間)に一括して解決させる。これにより、 $O(N^2)$ になりがちな処理コストを $O(N)$ へと押し下げる事が可能になる。
③ メモリリークを根絶するオブジェクトライフサイクル管理
VBAのガベージコレクションは非常に脆弱だ。特にVisioの `Page` や `Shape`、さらには `Selection` オブジェクトをループ変数として暗黙的に保持し続けると、COMの参照カウントが落ちず、バッチ処理が進むにつれてメモリリーク(VBAメモリ肥大化による「メモリ不足です」エラー)を引き起こす。
- ループ内で生成・取得したオブジェクト変数は、処理の単位ごとに `Set xxx = Nothing` で明示的に解放する。
- ドキュメントクローズ時は必ず `TargetDoc.Close` の後に変数を破棄する。
—
4. レガシー環境・システム間連携への応用
このアーキテクチャは、単体のVBAマクロに留まらない。例えば、C#やVB.NETなどの外だしアプリケーションから `Visio.Application` をCOMオートメーションで制御する際にも、全く同じ思想が適用できる。
外部プロセスからVisioを操作する場合、Visioのウィンドウがデスクトップ上に一瞬でも表示されると、フォーカス泥棒が発生し、サーバーサイドや無人バッチサーバー(RPA環境等)では致命的なエラーの原因となる。
`Visio.Invisible` フラグを用いた起動と、今回解説したサイレント設定を組み合わせることで、「人間が目視できない領域での完全な高速バッチエンジン」が完成する。
手動での作業や、画面を描画しながらの低速なマクロに依存する時代は終わった。
プロフェッショナルであれば、コードの裏側にある「Visioのエンジン挙動」を完全に支配し、リソースの限界を引き出すべきである。
