Visio VBAの限界を突破する:描画負荷を支配し、実行速度を極限まで引き上げるアーキテクチャ
Visioでの自動化において、中級者が陥る最大の罠は「オブジェクトを操作するたびに画面が再描画される」ことだ。10個や20個の図形なら無視できるが、千単位の図形を生成・編集する際、Visioの描画エンジンはメモリを喰らい、CPUを浪費し、最終的に「応答なし」の墓場へと我々を誘う。
本稿では、レガシーな環境であっても、Visioの描画ライフサイクルを完全に掌握するための極意を伝授する。
1. 描画負荷の根源を断つ:ScreenUpdatingとEventEnabled
Visioにおけるパフォーマンス低下の主因は、`Application.ScreenUpdating` の無効化を忘れること、そして `EventEnabled` の制御不足にある。
多くのエンジニアが `Application.ScreenUpdating = False` だけを行うが、それは半分正解で半分は不十分だ。真のシニアエンジニアは、「イベントの連鎖による再帰的な計算処理」を遮断する。
極限まで最適化したテンプレート構造
Public Sub OptimizedProcess()
Dim appVisio As Visio.Application
Set appVisio = Visio.Application
‘ 1. 画面更新の完全停止
Dim originalScreenUpdating As Boolean
originalScreenUpdating = appVisio.ScreenUpdating
appVisio.ScreenUpdating = False
‘ 2. イベント検知の無効化(これが重要:図形生成時のイベントトリガーを殺す)
Dim originalEventEnabled As Boolean
originalEventEnabled = appVisio.EventEnabled
appVisio.EventEnabled = False
On Error GoTo Cleanup
‘ — ここに重い描画処理を記述 —
Call BatchGenerateShapes(appVisio)
‘ ——————————
Cleanup:
‘ 3. 確実に状態を復元(例外発生時も考慮する)
appVisio.EventEnabled = originalEventEnabled
appVisio.ScreenUpdating = originalScreenUpdating
‘ 4. オブジェクトの明示的解放(VBAのガベージコレクションを信用するな)
Set appVisio = Nothing
End Sub
2. オブジェクトモデルの「重さ」を理解する
Visioのオブジェクトモデルにおいて、`Shape` オブジェクトへの直接アクセスは安くない。ループ内で `Page.Shapes` を繰り返し参照するのは、メモリ上のポインタを何度も辿らせる非効率的な行為だ。
パフォーマンスを改善する設計指針
- 参照のキャッシュ: ループ内で `ActivePage` や `Application` を呼び出してはならない。処理の開始前に変数へ格納せよ。
- イベントのハンドリング: `Document.EventList` に大量の `Event` オブジェクトを登録している場合、処理速度は指数関数的に低下する。一括処理時は一時的にイベントリスナーを解除する判断が必要だ。
- Window.Invalidate: 最後に一度だけ `ActiveWindow.Invalidate` を呼び出し、メモリ上の描画データを一気にフラッシュすることで、更新コストを最小化できる。
3. レガシー環境でのメモリマネジメント
長時間の自動化処理では、VBA内部のメモリリークが致命傷になる。特にCOMオブジェクトの参照が残ったままだと、Visioプロセスが肥大化し、最悪の場合はOSのリソース不足を引き起こす。
Windows APIによる強制的なメモリ解放のヒント
VBA単体では不十分な場合、`SetProcessWorkingSetSize` APIを使用して、ワーキングセットをトリミングする手法も存在するが、これは最終手段だ。
‘ メモリ最適化のためのAPI定義(必要な場合のみ使用)
If VBA7 Then
Private Declare PtrSafe Function SetProcessWorkingSetSize Lib “kernel32” _
(ByVal hProcess As LongPtr, ByVal dwMinimumWorkingSetSize As LongPtr, ByVal dwMaximumWorkingSetSize As LongPtr) As Long
End If
‘ 使用例:処理終了後にメモリを整理する
‘ Call SetProcessWorkingSetSize(-1, -1, -1)
4. チーフアーキテクトからの助言
「速いコード」を書くとは、単に処理を短くすることではない。「Visioという巨大なエンジンがいかに動いているか」を想像することだ。
描画を止めるのは、Visioに「余計な気を遣わせない」ためである。イベントを止めるのは、「背後で走る複雑なロジックを一時停止させる」ためである。これらはすべて、Visioの処理負荷を最小限に抑え、CPUリソースを純粋な計算処理に集中させるための戦略だ。
もしあなたが大規模な社内システムを保守しているのであれば、まずは「なぜこの処理が遅いのか」を推測するのではなく、`Application.ScreenUpdating` と `EventEnabled` の二重奏を疑え。それが、レガシーを克服する第一歩である。
—
追記:
次の記事では、ShapeSheetの数式を直接操作して、VBA側で計算を行わずに描画を完結させる「ShapeSheet駆動型自動化」の神髄について解説する予定だ。期待して待っていてほしい。
