Visio VBAの深淵:再帰的走査で「複雑な階層構造」を完全制御する
Visioの図面は、単なる図形の羅列ではない。それは、`Shapes`コレクションが織りなす「深い木構造」である。
グループ化された図形(Group)を操作しようとして、`Shapes.Item(i)`でループを回し、途方に暮れた経験はないか? グループの中にさらにグループがあり、その中にまたグループがある。階層が深くなればなるほど、単純なループは破綻し、コードはスパゲッティ化する。
真に自動化を極めるエンジニアは、ここで「再帰(Recursion)」を用いる。そして、メモリ管理とオブジェクトのライフサイクルを厳密に制御することで、巨大な図面ファイルでも一瞬で走査を完了させるのだ。
—
1. 再帰的走査のアーキテクチャ
Visioにおける再帰処理の骨子は、「対象がグループ(`Type = visTypeGroup`)であれば自分自身を呼び出し、そうでなければ処理を実行する」というシンプルな条件分岐に集約される。
しかし、現場レベルでは「特定の条件を満たすパーツだけを抽出したい」「特定のレイヤーにあるものだけを操作したい」といった制約が加わる。ここで重要なのは、オブジェクトの参照を保持しすぎないことだ。
実装の要諦
‘ 階層を問わず、図面内の全てのシェイプを走査するプロシージャ
Public Sub TraverseShapes(ByVal shps As Visio.Shapes)
Dim shp As Visio.Shape
‘ 逆順ループは必須ではないが、グループ解除や削除を行う場合は必須の作法
‘ パフォーマンスを考えれば、参照渡しを意識した記述が望ましい
For Each shp In shps
‘ 1. ここで現在のシェイプに対するロジックを実行
ProcessTargetShape shp
‘ 2. グループであれば再帰的に潜る
If shp.Type = Visio.VisShapeTypes.visTypeGroup Then
If shp.Shapes.Count > 0 Then
TraverseShapes shp.Shapes
End If
End If
‘ 3. 明示的な解放(特に巨大な図面では重要)
Set shp = Nothing
Next shp
End Sub
—
2. メモリ最適化と「目に見えないボトルネック」
VBAはガベージコレクションが脆弱だ。特に`Visio.Shape`オブジェクトを多用する再帰処理において、不用意なオブジェクトの生成はメモリリークを招き、VBAスタックオーバーフローや、最悪の場合はVisio自体のクラッシュを誘発する。
- `Set obj = Nothing` の徹底: ループ内で生成した一時的なシェイプ参照は、スコープを抜ける前に必ず破棄せよ。
- `EventsEnabled`の無効化: 大規模な図形操作を行う際、`Application.EventsEnabled = False` を利用せよ。個々のシェイプ変更時に発生するイベント(`ShapeAdded`や`ShapeChanged`)を抑制するだけで、実行速度は数倍向上する。
—
3. レガシー環境でのAPI活用:Windows APIによる「割り込み」
Visio VBAが標準で提供していない機能(ウィンドウの強制再描画の抑制や、特定プロセスの優先度制御など)が必要な場合、`Declare PtrSafe`によるWindows APIの呼び出しを恐れてはならない。
例えば、大規模な再帰処理中にVisioが「応答なし」と判定されるのを防ぐには、メッセージループを回す必要がある。
If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Private Declare PtrSafe Function DoEvents Lib “user32” () As Long ‘ 本来はVBA組み込みだが制御用
End If
‘ 処理が重い場合は、再帰の合間にDoEventsを挟む
‘ ただし、頻繁すぎるとパフォーマンスが劣化するため、
‘ カウンタで間隔を制御するのがシニアの流儀である
—
4. チーフアーキテクトからの提言:クリーンなコードのために
複雑な図形構造を扱う際、最も陥りやすい罠は「処理と走査の混在」である。
- 関数の疎結合化: `TraverseShapes`には「走査」のみを担当させ、個別のロジックはデリゲート(あるいはコールバック風の別関数)に切り出せ。
- 例外処理の構造化: 特定のシェイプでエラーが発生した際、スタック全体を崩壊させないよう、`On Error Resume Next`で誤魔化すのではなく、`Err.Number`を判定し、構造体(UDT)でステータスを管理するのだ。
結論:技術は「泥臭さ」を隠すためにある
グループ構造を再帰で解き明かす技術は、単なるアルゴリズムの知識ではない。それは、Visioという巨大で複雑なCOMコンポーネントの挙動を、あなたの意志に従わせるための「対話術」である。
もし、貴殿が管理しているシステムが大規模な図面を扱っているなら、まずは`Shapes.Count`がどれほどの負荷を生んでいるか、`Application.ScreenUpdating = False`を適切に配置できているか、再確認してほしい。
コードは、書くことよりも「消すこと」「洗練させること」に魂が宿る。
貴殿のVBAが、今日も静かに、しかし確実にシステムを駆動させることを願う。
