Visio VBAを掌握する極限の知見:Page.DropConnectedによる配線自動化の極意
アーキテクトたるもの、数千ノードに及ぶネットワーク図や複雑なBPMNプロセスフローの生成において、座標計算の泥沼に足を取られてはならない。
素人が書くVisioマクロは、決まってこうだ。
1. `Page.Drop` で図形を適当な座標に置く。
2. 返されたShapeオブジェクトのPinX/PinYを必死に計算し直す。
3. 最後に `Page.Drop` でコネクタを生成し、 `Connects` コレクションを手動でバインドする。
このアプローチは、コード行数が肥大化するだけでなく、Visioの図形エンジン(Visio Engine)に対して無駄な再計算イベントを連発させ、パフォーマンスを致命的に低下させる。実務において、数式ベースの座標計算をVBAで愚直に実装することは罪悪である。
今回は、Visio 2010以降の隠れた名メソッドでありながら、正しく扱えば圧倒的な省力化をもたらす `Page.DropConnected` の深層を解説する。オブジェクトのライフサイクル管理、そして配線自動化のパラダイムシフトをここに示す。
—
1. `Page.DropConnected` とは何か? なぜレガシーな手法を捨てるべきなのか
`Page.DropConnected` メソッドは、指定した「既存の図形(FromShape)」に対して、方向とマスターを指定して「新しい図形(TargetMaster)」をドロップし、さらに一撃でコネクタ(ConnectorMaster)による接続までを完了させるアトランティック(不可分)な操作である。
従来の接続処理の悪夢
従来の `Drop` メソッドと `Connect` オブジェクトの組み合わせでは、以下のようなボイラープレートコードが必須だった。
‘ 【アンチパターン】レガシーな手動接続の例
Dim shpFrom As Visio.Shape, shpTo As Visio.Shape, shpConn As Visio.Shape
Set shpFrom = ActivePage.Drop(Master1, 2, 5)
Set shpTo = ActivePage.Drop(Master2, 5, 5)
Set shpConn = ActivePage.Drop(Visio.Application.ConnectorMaster, 0, 0)
‘ 接続セルのグルーピング(Glue)
shpConn.CellsU(“BeginX”).GlueTo shpFrom.CellsU(“PinX”)
shpConn.CellsU(“EndX”).GlueTo shpTo.CellsU(“PinX”)
‘ ※この間、描画更新やレイアウト計算が走り、メモリとCPUを無駄に消費する
この方法では、図形間の距離、向き、そしてマスターのサイズに応じたオフセットをVBA側で完全に計算し尽くしていなければ、接着が外れたり(Dead Glue)、予期せぬ図形のワープ現象を引き起こす。
`DropConnected` の優位性
一方、`DropConnected` は、Visioエンジン側が自動的にレイアウト方向(上・下・左・右)を解釈し、適切なオフセットで図形を配置した上で、最適なコネクタを自動ルーティングした状態で生成する。
‘ 【推奨】DropConnectedによる一撃生成
Dim shpNew As Visio.Shape
Set shpNew = ActivePage.DropConnected(shpFrom, TargetMaster, Direction, ConnectorMaster)
開発者は座標計算の呪縛から解放され、「どの図形の、どの方向へ、何を繋ぐか」という論理構造の記述だけに集中できるのだ。
—
2. 実践:ネットワーク構成図を連鎖的・動的に構築するプロフェッショナルコード
以下のコードは、単なるAPIのラッパーではない。大規模なデータ構造(今回はハードコードされた配列だが、RDBやAPIからのJSON入力を想定してほしい)を読み込み、メモリリークを防ぐ厳格なオブジェクト管理のもとで、ネットワークトポロジを自動生成する実用コードである。
Option Explicit
‘ ==============================================================================
‘ モジュール名: modNetworkAutoLayout
‘ 概要 : Page.DropConnectedを活用したネットワーク図の連鎖的動的生成
‘ アーキテクト: 首席自動化エンジニア
‘ ==============================================================================
Public Sub GenerateNetworkTopology()
‘ 画面描画とイベントを停止し、処理速度を限界まで引き上げる
Application.ScreenUpdating = False
Application.EventsEnabled = False
Dim startTime As Double
startTime = Timer
On Error GoTo ErrorHandler
Dim vsoPage As Visio.Page
Set vsoPage = ActivePage
‘ マスターシェイプの事前ロード(毎回FindObjectする愚を犯さない)
Dim mstServer As Visio.Master, mstSwitch As Visio.Master, mstRouter As Visio.Master
Dim mstConnector As Visio.Master
Set mstRouter = GetMaster(“基本フローチャート図形”, “プロセス”) ‘ 例:ルーターの代わり
Set mstSwitch = GetMaster(“ネットワークおよび周辺機器”, “ハブ”)
Set mstServer = GetMaster(“コンピュータおよびモニター”, “サーバー”)
Set mstConnector = Application.ConnectorMaster ‘ 組み込みの動的コネクタ
‘ 1. 起点となるルート図形を配置 (Core Router)
Dim shpCoreRouter As Visio.Shape
Set shpCoreRouter = vsoPage.Drop(mstRouter, 4.25, 10.0)
shpCoreRouter.Text = “Core-Router-01”
‘ 2. コアからスイッチ群への連鎖生成 (下方向: visSLODown = 2)
Dim shpSwitch1 As Visio.Shape, shpSwitch2 As Visio.Shape
Set shpSwitch1 = vsoPage.DropConnected(shpCoreRouter, mstSwitch, visSLODown, mstConnector)
shpSwitch1.Text = “Access-SW-01”
Set shpSwitch2 = vsoPage.DropConnected(shpSwitch1, mstSwitch, visSLODown, mstConnector)
shpSwitch2.Text = “Access-SW-02”
‘ 3. スイッチからサーバー群への連鎖生成 (右方向: visSLORight = 4)
Dim i As Long
Dim shpCurrentServer As Visio.Shape
Dim shpTargetSwitch As Visio.Shape
For i = 1 to 3
‘ 奇数はSW1、偶数はSW2に接続
If i Mod 2 <> 0 Then
Set shpTargetSwitch = shpSwitch1
Else
Set shpTargetSwitch = shpSwitch2
End If
Set shpCurrentServer = vsoPage.DropConnected(shpTargetSwitch, mstServer, visSLORight, mstConnector)
shpCurrentServer.Text = “App-Server-0” & i
Next i
‘ 4. 自動レイアウトの適用(必要に応じ全体を整形)
vsoPage.Layout
‘ 完了ログ
Debug.Print “トポロジ生成完了. 処理時間: ” & Format(Timer – startTime, “0.00秒”)
CleanUp:
‘ 確実なオブジェクト解放と環境復元
Set shpCoreRouter = Nothing
Set shpSwitch1 = Nothing
Set shpSwitch2 = Nothing
Set shpCurrentServer = Nothing
Set shpTargetSwitch = Nothing
Set mstRouter = Nothing
Set mstSwitch = Nothing
Set mstServer = Nothing
Set mstConnector = Nothing
Set vsoPage = Nothing
Application.ScreenUpdating = True
Application.EventsEnabled = True
Exit Sub
ErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “Visio Automation Engine”
Resume CleanUp
End Sub
‘ ——————————————————————————
‘ 補助関数: 指定したステンシルから安全にマスターを取得する
‘ ——————————————————————————
Private Function GetMaster(ByVal stencilName As String, ByVal masterName As String) As Visio.Master
Dim vsoStencil As Visio.Document
On Error Resume Next
Set vsoStencil = Application.Documents(stencilName & “.vss”)
If vsoStencil Is Nothing Then
Set vsoStencil = Application.Documents.OpenEx(stencilName & “.vss”, visOpenRO + visOpenHidden)
End If
On Error GoTo 0
If vsoStencil Is Nothing Then
Err.Raise 9999, “GetMaster”, “ステンシル ‘” & stencilName & “‘ がロードできません。”
End If
Set GetMaster = vsoStencil.Masters(masterName)
End Function
—
3. チーフアーキテクトが教える:実運用における最適化と罠
このコードを本番環境(エンタープライズの自動レポート生成システムなど)に導入する際、直面するであろう「限界」と、それを突破するための知見を共有する。
1. 描画更新とイベントの完全無効化
大規模な図面生成において、`Application.ScreenUpdating = False` と `Application.EventsEnabled = False` の二重ガードは絶対条件である。これを行わないと、`DropConnected` が実行されるたびにVisioのGUIスレッドが再描画とシェイプシート(ShapeSheet)の計算を行い、処理時間が数十倍に跳ね上がる。
2. メモリリークの防止とオブジェクトの明示的解放
VBAのガベージコレクションは頼りにならない。特にCOMオブジェクトの参照カウントは、ループ内でローカル変数を再利用する際などに残りやすい。
生成したオブジェクトは、処理が終わったら速やかに `Set obj = Nothing` を明示的に行い、VBAの内部ヒープをクリーンに保つこと。これを怠ると、数千個の図形を生成するプロセスでメモリリーク(Out of Memory)を引き起こし、ExcelやVisioが突然沈黙する。
3. `Direction` パラメータの解釈の癖
`DropConnected` の第3引数(Direction)には以下の列挙体が使用できるが、図形のマスターの形状や端子(Connection Points)の定義によっては、開発者の意図しない方向に伸びることがある。
- `visSLORight` (4): 右方向
- `visSLOLeft` (3): 左方向
- `visSLODown` (2): 下方向
- `visSLOUp` (1): 上方向
もし意図しない挙動を示す場合、それはVBAのバグではなく、対象マスターシェイプ側が持つ「接続ポイントのメタデータ」の不備である。プロフェッショナルであれば、マスター側のコネクションポイントをあらかじめ検証・設計しておくべきだ。
—
終わりに:自動化の先にある領域
`Page.DropConnected` を使いこなすことは、単にコードを短くすることではない。それは「座標という物理的な制約」から解放され、「論理トポロジという抽象概念」をそのまま図面にコンパイルする能力を手に入れることを意味する。
レガシーなVBAスクリプトの迷宮から抜け出し、システムとデータが直接対話する洗練されたアーキテクチャを構築してほしい。君の書くマクロが、単なる「お助けツール」から、企業のインフラを支える「エンタープライズ・エンジン」へと昇華することを期待している。
