【テクニカル・上級編】大容量図面での処理速度を劇的に向上させる:Application.DeferRecalcによる描画・計算保留テクニック – Visio VBA解析バイブル

スポンサーリンク

序論:なぜあなたのVisioマクロは「数千個」の壁を越えられないのか

長年、エンタープライズ向けの自動図面生成システムを構築してきた者なら、誰もが一度は「数千個のシェイプを配置した瞬間に、Visioが凍りつく」という絶望に直面したことがあるはずだ。

初心者はこれを「PCのスペック不足」や「VBAの限界」と片付ける。しかし、我々プロフェッショナルは知っている。原因はハードウェアではなく、Visioの心臓部である「ShapeSheet」の過剰な反応性にある。

Visioの各シェイプは、単なる図形ではない。数千のセルが複雑にリンクし合う「スプレッドシートの集合体」だ。一つのシェイプの座標を動かすたびに、Visioは依存関係にあるすべての数式を再計算し、描画エンジンを駆動させ、トポロジーを更新しようとする。これを数千回繰り返せば、処理時間は指数関数的に増大する。

本稿では、この「再計算の地獄」を回避し、大規模図面処理を劇的な速度へと昇華させる究極の切り札、`Application.DeferRecalc` を中心とした、アーキテクトレベルの最適化技法を伝授する。

1. 内部構造の洞察:`DeferRecalc` が制御するもの

Visio VBAにおける `Application.DeferRecalc` プロパティは、Booleanではなく `Integer` (実際には `Short`) を取る。

  • `Application.DeferRecalc = 1`:再計算を保留する。
  • `Application.DeferRecalc = 0`:保留を解除し、蓄積されたすべての変更を一括で再計算する。

このフラグを立てている間、VisioはShapeSheetのセル値の変更を受け付けるが、それによって引き起こされる「連鎖的な再計算」と「ジオメトリの再構築」を一時停止する。これはExcelの `Calculation = xlManual` に似ているが、Visioにおいては描画エンジンのパイプラインそのものを一時的にバイパスするため、その効果はより劇的だ。

重要な注意点:エラーハンドリングの義務

`DeferRecalc` を `1` にしたままプロシージャが異常終了すると、ユーザーは「値を変更しても形が変わらない」という、壊れたVisio環境に取り残される。これは業務システムとしては致命的だ。必ず `Try-Catch`(VBAでは `On Error GoTo`)による確実な復帰を実装しなければならない。

2. 実戦的実装:高速化のテンプレート

以下に、私が大規模なネットワーク構成図やプラント設計図の自動生成で実際に使用している、堅牢な高速化ラッパーの構造を示す。

Option Explicit

‘ Windows APIによる高精度タイマー(パフォーマンス計測用)
Private Declare PtrSafe Function QueryPerformanceCounter Lib “kernel32” (lpPerformanceCount As Currency) As Long
Private Declare PtrSafe Function QueryPerformanceFrequency Lib “kernel32″ (lpFrequency As Currency) As Long

”’

”’ 大容量シェイプ一括生成のプロフェッショナル・パターン
”’

Public Sub MassiveShapeProcessing()
Dim vsoApp As Visio.Application
Set vsoApp = Visio.Application

Dim vsoPage As Visio.Page
Set vsoPage = vsoApp.ActivePage

‘ 1. 最適化前の状態をキャッシュ
Dim previousScreenUpdating As Boolean: previousScreenUpdating = vsoApp.ScreenUpdating
Dim previousEventsEnabled As Boolean: previousEventsEnabled = vsoApp.EventsEnabled

‘ 2. 極限の高速化設定
On Error GoTo ErrorHandler
vsoApp.ScreenUpdating = False ‘ 画面描画の停止
vsoApp.EventsEnabled = False ‘ イベントハンドラの停止(アドイン等の干渉防止)
vsoApp.DeferRecalc = 1 ‘ 【最重要】再計算の保留

‘ — 処理開始 —
Dim i As Long
Dim vsoShape As Visio.Shape
Dim startPos As Double

For i = 1 To 5000
‘ シェイプのドロップ
‘ 注意: 大量処理時は、マスターシェイプからのDropよりも、
‘ 可能な限り一括で処理できるメソッド(DropManyなど)を検討すること
Set vsoShape = vsoPage.Drop(vsoApp.Documents(“BASIC_U.VSSX”).Masters(“Rectangle”), i 0.1, i 0.1)

‘ ShapeSheetへの直接書き込み
‘ Cellオブジェクトを介さず、CellsSRCでインデックス指定するとさらに速い
vsoShape.Cells(“Width”).FormulaU = “20mm”
vsoShape.Cells(“Height”).FormulaU = “10mm”
vsoShape.Text = “Node ” & i

‘ メモリリーク対策:不要なオブジェクト参照を明示的に解放
‘ ループ内でのCOMオブジェクトの蓄積はメモリプレッシャーを招く
Set vsoShape = Nothing

‘ 進捗表示(100個おきなど、頻度を抑えること)
If i Mod 100 = 0 Then
DoEvents
Debug.Print i & ” items processed…”
End If
Next i
‘ — 処理終了 —

CleanUp:
‘ 3. 状態の復元(逆順で行うのが定石)
If Not vsoApp Is Nothing Then
vsoApp.DeferRecalc = 0 ‘ 再計算の実行
vsoApp.EventsEnabled = previousEventsEnabled
vsoApp.ScreenUpdating = previousScreenUpdating
End If
Exit Sub

ErrorHandler:
Debug.Print “Error: ” & Err.Description
Resume CleanUp
End Sub

3. アーキテクトの視点:さらなる次元の最適化

`DeferRecalc` を導入してもなお速度が足りない場合、以下の領域に踏み込む必要がある。

A. `DropMany` メソッドの活用

VBAのループ内で `Page.Drop` を繰り返すのは、依然としてオーバーヘッドが大きい。Visioには `Page.DropMany` という、マスターシェイプの配列と座標の配列を一括で渡し、内部エンジン側で一気に生成するメソッドが存在する。これと `DeferRecalc` を組み合わせるのが、Visio VBAにおける最速の解である。

B. COM参照のライフサイクル管理

VBAからVisioを操作する場合、生成された各 `Shape` オブジェクトはCOM参照としてカウントされる。ループ内で `Set vsoShape = …` を繰り返す際、明示的に `Nothing` を代入しないと、ガベージコレクションのタイミングまでメモリ上に残骸が残り、数千件規模ではワーキングセットを圧迫する。

C. Windows APIによる描画固定(禁じ手)

`Application.ScreenUpdating = False` でも不十分なほどUIが明滅する場合、Windows APIの `SendMessage` を使用して、Visioのメインウィンドウハンドル(`vsoApp.WindowHandle32`)に対して `WM_SETREDRAW` を発行し、OSレベルで描画をロックする手法がある。ただし、これはOSのメッセージキューを操作するため、極めて慎重な実装が求められる。

4. 結論:制御不能な自動化を、統制されたプロセスへ

`Application.DeferRecalc` は、単なる高速化スイッチではない。それは、Visioという「リアクティブな情報の海」において、エンジニアが主導権を握るための「時間の静止命令」である。

大規模システムにおいて、処理速度は単なる利便性ではなく「信頼性」そのものだ。処理に5分かかるマクロはユーザーに敬遠され、やがて使われなくなる。しかし、適切な最適化によってそれを30秒に短縮できれば、それは「業務の基盤」へと昇格する。

あなたが書くコードの一行が、ShapeSheetの数千のセルの連鎖を制御しているという自覚を持ってほしい。その時、あなたのVisio VBAは、単なるスクリプトから真の「エンジニアリング・ツール」へと進化するだろう。

タイトルとURLをコピーしました