【テクニカル・上級編】非表示作業用ページ(Hidden Page)の動的作成と活用:ユーザーに見せずに一時的な計算・図形加工を行うVBAサンドボックス手法 – Visio VBA解析バイブル

スポンサーリンク

Visio VBAを掌握する極限の知見:非表示作業用ページ(サンドボックス手法)による高速描画と幾何計算の極意

Visio VBAによる大規模な図面自動生成において、最大の敵は「画面のちらつき(ScreenUpdate)」と「不必要な再計算・再描画コスト」である。
形状(Shape)を1つ追加・変形するたびにVisioのGUIがそれを追跡し、UIスレッドをブロックしながらDOM(Document Object Model)を更新していく。この愚直なアプローチをとる限り、数千個のオブジェクトを扱うエンタープライズシステムでは、実行完了までに数分を要する無残な結果となる。

この呪縛から脱却する唯一にして最良の解が、「非表示作業用ページ(Hidden Page / サンドボックス手法)」の動的構築と活用だ。

本稿では、ユーザーの目に触れない隔離された空間で複雑なブール演算、形状のプリファブ(事前組み立て)、幾何計算を行い、完成した成果物だけを一撃で本番ページへ転送・破棄する、プロフェッショナルな設計思想と実装コードを詳解する。

1. なぜ「サンドボックス手法」が必要なのか?

オブジェクトモデルの裏側にある描画スレッドの挙動

Visioの `Page` オブジェクトは、常にウィンドウ(Window)と結びついているわけではない。
メモリ上にのみ存在する `Page` を動的に生成し、`Window.Visible = False` やページ自体の背景設定を制御することで、GUIの再描画イベント(`Visio.Elected` や `SelectionChanged` など)を完全にバイパスできる。

画面描画抑止(ScreenUpdating)の限界

よくある誤解として、`Application.ScreenUpdating = False` さえ入れておけば安全だというものがある。しかし、複雑な図形同士の結合(Union)、切り抜き(Fragment)、あるいは大量のシェイプシート関数の評価が絡む場合、可視ページ上での操作は依然として内部の空間インデックス(R-Treeなど)の頻繁な再構築を強いる。

サンドボックスページ上でこれらを完結させれば、空間インデックスの構築範囲をその一時ページ内に限定でき、本番ページのパフォーマンス劣化を完全に防ぐことができる。

2. 実装アーキテクチャ:サンドボックスのライフサイクル管理

堅牢なVBAシステムにおいて、例外発生時の「ゴミ(一時ページ)の残留」は絶対に避けなければならない。
エラーハンドリングのスコープ内で必ずページを破棄する、厳格なRAII(Resource Acquisition Is Initialization)的アプローチをVBAの `On Error Goto` で模倣する。

実装コード:堅牢な非表示サンドボックスの骨格

以下のコードは、一時的な作業ページを動的に生成し、安全に破棄するためのテンプレートである。

Option Explicit

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

‘ パフォーマンス最大化のための基本設定
Dim origScreenUpdating As Boolean
Dim origEventEnabled As Boolean
origScreenUpdating = vsoApp.ScreenUpdating
origEventEnabled = vsoApp.EventEnabled

vsoApp.ScreenUpdating = False
vsoApp.EventEnabled = False

Dim vsoDoc As Visio.Document
Set vsoDoc = vsoApp.ActiveDocument

Dim vsoSandboxPage As Visio.Page
Set vsoSandboxPage = Nothing

On Error GoTo ErrorHandler

‘ 1. サンドボックスページの動的生成
Set vsoSandboxPage = vsoDoc.Pages.Add()
vsoSandboxPage.Name = “TEMP_SANDBOX_” & Format(Timer, “0.000”)

‘ ページを非表示(バックグラウンド等、UIから隠蔽)にする設定
‘ ※Visioの仕様上、ページ自体を完全な「不可視」にはできないため、
‘ 名前にプレフィックスを付け、描画先ウィンドウの視野外・非アクティブとして扱う。

‘ — 【サンドボックス内での高負荷処理】 —
Call ProcessHeavyGeometry(vsoSandboxPage, vsoDoc.Pages.Item(1))
‘ ——————————————-

ErrorHandler:
‘ 2. 例外有無に関わらず、必ずサンドボックスを破棄(メモリリーク防止)
On Error Resume Next
If Not vsoSandboxPage Is Nothing Then
vsoSandboxPage.Delete visDeleteFired
End If

‘ 設定の復元
vsoApp.ScreenUpdating = origScreenUpdating
vsoApp.EventEnabled = origEventEnabled

If Err.Number <> 0 Then
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical
End If
End Sub

3. 実践:複雑な形状のプレ組み立てと一括転送(Drop/DropMany)

では、このサンドボックス上で具体的に何を行うべきか。
例えば、「数個のマスターシェイプを結合し、特定のオフセットで変形させ、テキストを埋め込んだカスタムパーツ」を大量に生成するケースを考える。

可視ページ上でこれを行うと、パーツが1つずつ画面に現れては消え、レイアウトがガタつく。サンドボックス上で完成させ、それを `Shape.Drop` または `Shape.SpatialNeighbors` 等で一括して本番ページへ移す。

プレハブ組み立てと転送のロジック

Private Sub ProcessHeavyGeometry(ByRef vsoSandbox As Visio.Page, ByRef vsoTargetPage As Visio.Page)
Dim shp1 As Visio.Shape
Dim shp2 As Visio.Shape
Dim shpCompound As Visio.Shape

‘ サンドボックス上で図形を配置・演算
Set shp1 = vsoSandbox.DrawRectangle(0, 0, 5, 3)
Set shp2 = vsoSandbox.DrawOval(2, 2, 6, 5)

‘ シェイプのブール演算(例:結合)
‘ ※VisioのCOMでは Shape.Union や Shape.Fragment を使用
Dim shpArray(0) As Visio.Shape
Set shpArray(0) = shp2

‘ shp1に対してshp2を結合する
‘ (実際のコードではエラーハンドリングやグループ化の状態に注意が必要)
‘ Set shpCompound = shp1.Union(shpArray)

‘ 出来上がった完璧なプレハブを、本番ページへコピー
‘ Copy/Pasteのクリップボード経由は速度が落ちるため、極力オブジェクトの参照・複製を利用する
Dim vsoClonedShapes As Visio.Selection
vsoSandbox.CreateSelection(visSelTypeByLayer, visSelLayerAll, “”).SelectAll

‘ 本番ページへ一括ドロップ(あるいはDropManyによる座標指定配置)
‘ ここでは簡略化として、生成されたシェイプ群を本番ページに移動
Dim i As Long
Dim vsoShapes As Visio.Shapes
Set vsoShapes = vsoSandbox.Shapes

For i = 1 To vsoShapes.Count
vsoShapes(i).Copy
vsoTargetPage.Paste
‘ ペーストされた図形を所定の位置へ移動…
Next i
End Sub

4. チーフアーキテクトが教える:極限のメモリ最適化とVBAの罠

長年VisioアドインやVBAシステムを構築してきた中で、避けて通れない「メモリリークとCOMの参照管理」の暗黙のルールに言及する。

1. オブジェクト変数の明示的な `Nothing` 解放

VBAのガベージコレクションは非常に頼りない。特にVisioのオブジェクトモデル(`Shapes`, `Cell`, `Row` など)は、ループ内でインスタンスを取得し続けると、COMラッパーの参照カウンタが溢れ、メモリリークや「HRESULT: 0x80010108 (RPC_E_DISCONNECTED)」といった致命的なオートメーションエラーを引き起こす。

> 鉄則: ループ内で生成・取得したオブジェクト変数は、反復の都度 `Set variable = Nothing` で明示的に解放せよ。

Dim i As Long
Dim shp As Visio.Shape
Dim vsoShapes As Visio.Shapes
Set vsoShapes = vsoSandboxPage.Shapes

For i = 1 To vsoShapes.Count
Set shp = vsoShapes(i)
‘ 何らかの処理
‘…

‘ ループの最後で必ず切断
Set shp = Nothing
Next i
Set vsoShapes = Nothing

2. ウィンドウイベントと再計算の完全隔離

`vsoApp.EventEnabled = False` を指定しても、一部のVBAイベントや外部COM連携アドインからのフックは生き続ける場合がある。
サンドボックスページを作成する際は、そのページが属するドキュメントの `EventList` や、計算用の一時的な数式(FormulaU)評価における不要なイベントトリガーを抑制するため、極力 `FormulaU` ではなく `ResultIU` などを使い、シェイプシートの遅延評価(Deferred Evaluation)を利用する。

5. まとめ

非表示作業用ページ(サンドボックス手法)は、単なる「画面を隠すための小技」ではない。
これは、Visioという重厚なベクターグラフィックスエンジンを、あたかもヘッドレスサーバーのように酷使し、最大限のパフォーマンスを引き出すための高度なアーキテクチャパターンである。

画面のちらつきに悩み、数千個の図形生成でプログレスバーがフリーズする悪夢から抜け出したいのであれば、今すぐあなたのVBAコードベースに「サンドボックス層」を導入せよ。
コードの美しさと圧倒的な実行速度が、プロフェッショナルとしての仕事の質を雄弁に物語るはずだ。

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