Visio VBAを掌握せよ:Shapeコレクション走査における「逆順ループ」の絶対原則
Visioのオートメーションを扱う際、初心者が必ず踏み抜く地雷がある。それが「図形の削除・追加によるコレクション走査の破綻」だ。
「なぜか一部の図形が消し残る」「なぜかインデックス範囲外エラー(Runtime Error 9)が発生する」。これらのバグは、単なる実装ミスではない。Visioのオブジェクトモデルが持つ「動的なインデックス管理」という仕様に対する、理解不足の結果である。
今日は、伝説的なアーキテクチャの現場で叩き込まれる、この「走査の極意」を伝授する。
—
1. なぜ「順方向ループ」が死を招くのか
`For i = 1 To Page.Shapes.Count` というコードを書いていないだろうか?
これがなぜ危険なのか、その挙動を物理的なメモリモデルから紐解く。
Visioの `Shapes` コレクションは、インデックス(1から始まる)を保持している。ここで、インデックス `i` 番目の図形を削除すると、`i+1` 以降のすべての図形は、自動的にインデックスを一つ前へ「繰り上げる」。
このとき、ループカウンタ `i` は次のインデックス `i+1` を指そうとするが、先ほどまで `i+1` だった図形は、繰り上げによって `i` に移動している。結果として、「隣り合っていたはずの一つの図形が、走査の網から漏れる」という事象が発生する。
これは単なるバグではない。複雑なビジネスロジックにおいて、この「スキップ」はデータ整合性の破壊を意味する。
2. 逆順ループ(Step -1)こそが正義である理由
解決策は、インデックスの繰り上げの影響を「未来」に与えないこと。つまり、後ろから前へ走査する(Step -1)ことだ。
後ろから削除していけば、インデックスが繰り上がろうが、それは既に処理済みの領域への影響に過ぎない。これから処理すべき図形のインデックスには一切の影響を与えないのだ。
【実装:安全かつ堅牢な削除ループ】
‘ @description: 指定したページ内の特定条件を満たす図形を安全に削除する
‘ @author: Chief Architect
Public Sub SafeDeleteShapesByCriteria()
Dim pg As Visio.Page
Dim shp As Visio.Shape
Dim i As Long
Set pg = ActivePage
‘ 逆順ループ:Countから1までを下向きにカウントする
‘ これにより、削除後のインデックスシフトの影響を完全に遮断する
For i = pg.Shapes.Count To 1 Step -1
Set shp = pg.Shapes(i)
‘ パフォーマンスへの配慮:図形名やカスタムプロパティでの条件判定
‘ 可能な限り軽いプロパティへのアクセスを優先する
If shp.Name Like “Target_” Then
‘ オブジェクトの明示的削除
shp.Delete
End If
‘ ループ内でのオブジェクト解放(VBAのメモリ管理の鉄則)
Set shp = Nothing
Next i
Set pg = Nothing
End Sub
—
3. チーフアーキテクトからの「極限の知見」
現場で生き残るシステムを作るためには、単に動くコードを書くだけでは不十分だ。以下の3点に注意せよ。
① オブジェクトの明示的解放(Nothingへのセット)
VBAのガベージコレクションは頼りにならない。特にCOMオブジェクトであるVisioのShapeは、明示的に `Set = Nothing` しなければ、長大なループ実行時にメモリリークを引き起こし、最終的にExcelやVisioプロセスがクラッシュする。
② 画面描画の抑止(Application.ScreenUpdating)
大規模な図面で図形を一つずつ消去する際、`Application.ScreenUpdating = False` を忘れてはならない。これを怠ると、GUIの再描画コストがCPUを占有し、処理速度が指数関数的に低下する。
③ Windows APIでの監視(上級者向け)
もし、非常に大規模な図面で図形の追加・削除を繰り返す必要があるなら、`Events` クラスを利用して `ShapeAdded` や `BeforeShapeDelete` をフックすることを検討せよ。ループの中だけで処理を完結させるのは、図形数が1,000を超える辺りから限界が来る。
結論
「インデックスの繰り上げ」を理解し、`Step -1` を使いこなすこと。これは、Visio VBAを自在に操るための、最も基礎的でありながら最も重要な「規律」だ。
コードは美しく、そしてロジックは冷徹であれ。
レガシーな環境であっても、設計の質がシステムの寿命を決める。次にコードを書くとき、君がこの原則を思い出してくれることを期待している。
