Visio VBAの深淵:Page.FindShapesが解き放つ「真の高速検索」アーキテクチャ
Visio開発において、初心者は決まって「全シェイプの総当たりループ」という罠に落ちる。`Page.Shapes`コレクションを回し、すべての`Shape`オブジェクトに対して`If`文で条件判定を行う……。小規模な図面ならそれでいい。だが、シェイプ数が数千を超え、複雑なグループ化が入り乱れるプロダクション環境では、それはただの「計算資源の浪費」であり、システム障害の温床だ。
今日は、Visioの描画エンジンが持つネイティブな検索能力、`Page.FindShapes`を最大限に活用し、泥臭いループ処理を排した「極限のフィルタリング」を伝授する。
—
1. なぜ「全探索」が罪深いのか
Visioのオブジェクトモデルにおいて、`Shape`オブジェクトへのアクセスはCOMラッパーを介した重い処理だ。ループ内で毎回プロパティを参照することは、メモリ管理上、極めてコストが高い。
一方、`Page.FindShapes`メソッドは、Visioの内部エンジンが持つインデックスを直接叩く。これは単なるVBAのメソッドではなく、APIレベルで最適化されたフィルタリングだ。特に大規模図面において、この「API直結型」の処理は、純粋なVBAループと比較して10倍から100倍のパフォーマンス差を生む。
—
2. 鋼鉄の検索アルゴリズム:FindShapesの実装
`FindShapes`は強力だが、その仕様には癖がある。フラグのビット演算を理解せざる者には扱えない。以下に、特定のテキストを持つシェイプを瞬時に特定する、プロダクション品質のコードを示す。
‘ 伝説的な検索アーキテクチャ:FindShapesによる高速抽出
Public Sub ExtractShapesByText(targetText As String)
Dim vsoPage As Visio.Page
Dim vsoShapes() As Visio.Shape
Dim i As Long
Set vsoPage = ActivePage
‘ VISFindShapesFlags:
‘ visFindShapesText – テキスト検索
‘ visFindShapesIncludeSubShapes – グループ内の子シェイプも検索対象に
‘ visFindShapesDeep – 再帰的検索
Dim flags As Long
flags = Visio.VisFindShapesFlags.visFindShapesText Or _
Visio.VisFindShapesFlags.visFindShapesIncludeSubShapes
‘ APIレベルの高速フィルタリング実行
‘ マッチしたシェイプは配列として返される
vsoShapes = vsoPage.FindShapes(flags, targetText, “”)
‘ 配列が空でないことを確認(メモリ安全性の確保)
If (Not vsoShapes) <> -1 Then
For i = LBound(vsoShapes) To UBound(vsoShapes)
‘ ここで得られるのは既にフィルタリング済みのオブジェクト
Debug.Print “Found Shape: ” & vsoShapes(i).Name
‘ 処理後は明示的に解放し、参照カウンタを制御する
Set vsoShapes(i) = Nothing
Next i
End If
‘ オブジェクトのクリーンアップ
Set vsoPage = Nothing
End Sub
このコードの極意
- フラグの選択: `visFindShapesDeep`を安易に使うと、階層が深い図面でオーバーヘッドが増える。必要最小限のフラグを選択せよ。
- 配列の生存期間: `vsoShapes`は`Shape`オブジェクトの配列として返される。処理が終わった後は、必ず各要素を`Nothing`に倒し、COM参照を解放する。これを怠ると、巨大図面ではメモリリークが即座に発生する。
—
3. レガシー環境でのパフォーマンスと安定性
VisioのVBAは、32bit時代から続く「呪縛」を抱えている。特に大規模なシステム連携を行う場合、`FindShapes`だけでは解決できない事象(例えば、特定のシェイプデータ/カスタムプロパティを条件にしたい場合)に直面する。
その際は、「FindShapesによる一次フィルタリング」+「軽量な属性判定」という二段構えの戦略をとれ。
1. 一次選別: `FindShapes`で候補を数百個に絞る。
2. 二次選別: 絞り込んだオブジェクトのみに対し、`Shape.CellExists`等を用いてプロパティを確認する。
これにより、全シェイプに対して重い`CellExists`を投げる愚を回避できる。
—
4. 最後に:エンジニアとしての矜持
VBAはレガシーではない。適切に設計され、オブジェクトのライフサイクルを深く理解したコードは、現代のどの高水準言語よりも「図面というドメイン」においては高速だ。
`FindShapes`を使いこなすということは、Visioという巨大なブラックボックスの「検索インデックス」を理解するということだ。安易なループを卒業し、エンジンの内側を叩くコードを書け。それが、この過酷な業務自動化の現場で生き残るための、唯一の道である。
次は、`EventSink`を用いたリアルタイム監視と、この`FindShapes`を組み合わせた「非同期オートメーション」について語ることにしよう。準備はいいか。
