Visioの限界を突破せよ:DropManyUによる「超高速」図形配置の極意
Visioのオートメーションにおいて、初心者が陥る最大の罠は「ループ内で`Drop`メソッドを連打する」という愚行だ。数百個のシェイプを配置する際、`For Each`の中でシェイプを生成していては、Visioの描画エンジンとCOMインターフェースのオーバーヘッドで時間は溶け、メモリは断片化し、最終的には「VBAは遅い」という無知な結論に達する。
真のエンジニアは、単なる機能実装ではなく、「Visioの内部トランザクションをいかに効率的に叩くか」に心血を注ぐ。今回は、`Page.DropManyU`を用いた、メモリ効率と速度を極限まで高めたバッチ配置の真髄を伝授する。
—
1. なぜ「DropManyU」なのか:オーバーヘッドの最小化
`Drop`メソッドを繰り返すと、その都度、イベントの発生、Undoスタックへの書き込み、描画更新が行われる。これに対し、`DropManyU`は一度の呼び出しで複数のオブジェクトをメモリ空間へ流し込む。
このメソッドを使う際、以下の3つの配列を準備する必要がある。
1. Masters配列: 配置するマスターシェイプの参照
2. XY配列: 配置する座標 (x, yの順でフラットに格納)
3. ObjectIDs配列: 生成されたシェイプのIDを受け取るためのバッファ
これらを型安全に、かつメモリ消費を抑えて処理することが、システムを堅牢にする鍵だ。
—
2. 極限の最適化を施した実装コード
以下に、数百個単位のノードを瞬時に配置するプロシージャを示す。ここでは、不要なオブジェクト参照を即座に破棄し、VBAのガベージコレクションを待たずにメモリを解放するプログラミングスタイルを徹底している。
‘ @description 大量シェイプを高速配置する最適化ルーチン
‘ @param targetPage 対象のVisioページ
‘ @param masterObj 配置するマスター
‘ @param xyArray 座標配列(0 to 2n-1)
Public Sub FastDropShapes(ByRef targetPage As Visio.Page, _
ByRef masterObj As Visio.Master, _
ByRef xyArray() As Double)
Dim count As Long
count = (UBound(xyArray) + 1) / 2
‘ 生成されるシェイプのIDを格納する配列
Dim idArray() As Integer
ReDim idArray(0 To count – 1)
‘ Masters配列の構築
Dim masterArray() As Object
ReDim masterArray(0 To count – 1)
Dim i As Long
For i = 0 To count – 1
Set masterArray(i) = masterObj
Next i
‘ トランザクションの開始(Undoの集約による速度向上)
Dim undoScope As Long
undoScope = Application.BeginUndoScope(“BatchDrop”)
‘ DropManyUの実行
‘ 第1引数: Masters, 第2引数: XY座標, 第3引数: ID格納配列
targetPage.DropManyU masterArray, xyArray, idArray
‘ トランザクション終了
Application.EndUndoScope undoScope, True
‘ メモリ最適化: オブジェクト配列の明示的解放
For i = 0 To count – 1
Set masterArray(i) = Nothing
Next i
Erase masterArray
Erase idArray
End Sub
—
3. 実践における「アーキテクトの視点」
イベント抑制によるパフォーマンスブースト
大量のシェイプを配置する際、`QueryCancelSelectionDelete`や`ShapeAdded`といったイベントが全シェイプに対して発火される。これが処理を重くする隠れた犯人だ。
バッチ処理の前後で `Application.EventState = visEventDisabled` を設定し、イベントを一時的に殺すことで、パフォーマンスは劇的に向上する。ただし、これにはリスクが伴うため、必ずエラーハンドリング内で確実にイベントを再開させること。
メモリの断片化(Fragmenting)対策
数百個のシェイプを生成すると、VBAのメモリ領域は断片化する。これを防ぐには:
- 配列の動的リサイズを避ける: `ReDim Preserve`は厳禁だ。必要なサイズを算出し、一度のメモリ確保で済ませる。
- Objectの参照カウント管理: `Set = Nothing`を徹底し、スコープを抜ける前に明示的にCOM参照を解放する。
レガシー環境でのAPI呼び出し
もし、配置計算に高度な幾何学的計算が必要な場合、VBAで計算するのは避けろ。計算はDLL化されたC++/C#で行い、結果だけをVBAへ渡す。Windows APIを用いてメモリ共有を行うことも可能だが、それは「最後の手段」だ。まずは`DropManyU`の限界まで使い倒すことが、保守性を維持するエンジニアの矜持である。
—
結論:技術は「書き方」ではなく「作法」である
Visio VBAは古い言語だが、その背後にあるCOMアーキテクチャは未だに強力だ。`DropManyU`のようなメソッドを使いこなすことは、単なるスピードアップではなく、「Visioの内部構造に対して敬意を払うこと」と同義である。
コードを書くときは、常にその背後で何が動いているかを想像せよ。それが、システムを「動くもの」から「止まらない資産」へと昇華させる唯一の道だ。
貴殿のプロジェクトが、この最適化によって数秒の処理短縮だけでなく、数時間のストレスから解放されることを願う。
