【上級者向け】SQL Serverデータ駆動型:Visio図面自動生成パイプラインの設計と実装
こんにちは。チーフアーキテクトの私だ。
これまで数多くの大規模な業務自動化案件を手掛けてきたが、その中でも特に問い合わせが多いのが「データベースのマスターデータや構成情報から、Visio図面を完全に自動生成・更新したい」という要件だ。
Excelからシェイプを読み込むようなお遊戯レベルの自動化であれば、ネットのサンプルをコピペすれば動くだろう。しかし、エンタープライズ環境において、SQL Server等のRDBからミリ秒単位でデータを取得し、数千個におよぶシェイプを正確な座標・接続関係で描画し、サイレントでPDF化して保存する――このパイプラインを構築するには、VisioのオブジェクトライフサイクルとCOMのメモリ管理、そしてトランザクションの概念を骨の髄まで理解していなければならない。
今回は、現場で即座に使えるプロダクションコードとともに、絶対に踏んではならない地雷と、それを回避するための極限の知見を伝授しよう。
—
1. なぜ「力技のVBA」は破綻するのか?(アーキテクチャの要諦)
多くのエンジニアが陥る罠が、`Recordset`をループさせながら、その都度Visioの画面を更新させ、マスターステンシルから非効率なシェイプのドロップを繰り返す実装だ。
これをやると、以下の致命的な問題が発生する。
1. 描画のオーバーヘッド(画面のちらつきと極端な速度低下)
2. ADO接続のリークとCOMオブジェクトの解放漏れによるメモリバースト
3. トランザクション不在による、途中でエラー落ちした際の半端な図面ファイルの生成
我々が目指すべきは、「完全なサイレント処理」「一括バッチ処理」「厳格なエラーハンドリングとクリーンアップ」を兼ね備えた堅牢なパイプラインである。
—
2. 堅牢な自動生成パイプライン:プロダクションコード
以下のコードは、SQL Serverから機器構成データを取得し、指定されたテンプレート(.vstx)をベースに図面を動的生成、最終的にPDFとして吐き出してサイレント終了するモジュールだ。
開発環境のVBAエディタで、ツール > 参照設定 から 「Microsoft ActiveX Data Objects 6.1 Library」 (または環境に合わせたバージョン)にチェックを入れていることを前提とする。
Option Explicit
‘ =========================================================================
‘ 処理名: SQL Serverデータ駆動型 Visio図面自動生成エンジン
‘ 概要: ADO経由でRDBから座標・接続情報を取得し、VSDXの生成からPDF出力までを実行
‘ =========================================================================
Public Sub GenerateDiagramFromDatabase()
‘ — 1. 接続文字列とファイルパスの定義 (環境に合わせて変更すること) —
Const DB_CONNECTION_STRING As String = “Provider=SQLOLEDB;Data Source=SRV-DB01;Initial Catalog=NetworkInventory;User Id=sa;Password=your_password;”
Const TEMPLATE_PATH As String = “C:\Enterprise\Templates\NetworkBase.vstx”
Const OUTPUT_VSDX_DIR As String = “C:\Enterprise\Output\VSDX\”
Const OUTPUT_PDF_DIR As String = “C:\Enterprise\Output\PDF\”
Dim targetDateStr As String
targetDateStr = Format(Now, “YYYYMMDD_HHNNSS”)
Dim outputVsdxPath As String
Dim outputPdfPath As String
outputVsdxPath = OUTPUT_VSDX_DIR & “NetworkMap_” & targetDateStr & “.vsdx”
outputPdfPath = OUTPUT_PDF_DIR & “NetworkMap_” & targetDateStr & “.pdf”
‘ — 2. ADO関連変数の宣言 —
Dim conn As Object
Dim rsDevice As Object
Dim rsConnection As Object
‘ — 3. Visio関連変数の宣言 —
Dim appVisio As Visio.Application
Dim docTarget As Visio.Document
Dim pgActive As Visio.Page
‘ — 4. エラーハンドリングの準備 —
On Error GoTo ErrorHandler
‘ 処理の高速化とサイレント化の極意:画面描画と警告を完全にシャットアウトする
‘ これを行わないと、生成スピードが10分の1以下に落ちる
Set appVisio = New Visio.Application
appVisio.ScreenUpdating = False
appVisio.Visible = False
appVisio.AlertsEnabled = False
‘ テンプレートから新規ドキュメントを作成 (既存テンプレートを汚さない)
Set docTarget = appVisio.Documents.Add(TEMPLATE_PATH)
Set pgActive = docTarget.Pages(1)
‘ — 5. データベース接続とデータ取得 —
Set conn = CreateObject(“ADODB.Connection”)
conn.CursorLocation = 3 ‘ adUseClient
conn.Open DB_CONNECTION_STRING
‘ 機器マスターの取得
Set rsDevice = CreateObject(“ADODB.Recordset”)
rsDevice.Open “SELECT DeviceID, ShapeName, MasterName, PosX, PosY, IPAddress FROM T_Devices WHERE IsActive = 1”, conn, 1, 1
‘ — 6. シェイプの動的配置ロジック —
Dim shpCurrent As Visio.Shape
Dim masterName As String
Dim posX As Double, posY As Double
‘ 効率的なシェイプドロップのためのマスターキャッシュ用
Dim mstTarget As Visio.Master
Do While Not rsDevice.EOF
masterName = Nz(rsDevice.Fields(“MasterName”).Value, “Server”)
posX = CDbl(Nz(rsDevice.Fields(“PosX”).Value, 0.0))
posY = CDbl(Nz(rsDevice.Fields(“PosY”).Value, 0.0))
‘ マスターシェイプの取得とドロップ
‘ ドキュメントのステンシルからマスターを取得する
On Error Resume Next
Set mstTarget = docTarget.Masters(masterName)
On Error GoTo ErrorHandler
If Not mstTarget Is Nothing Then
Set shpCurrent = pgActive.Drop(mstTarget, posX, posY)
‘ シェイプのシェイプデータ(カスタムプロパティ)に値をバインド
‘ セルが存在するか確認してから書き込むのがプロの作法
If shpCurrent.CellExists(“Prop.DeviceID”, 0) Then
shpCurrent.Cells(“Prop.DeviceID”).FormulaU = “””” & rsDevice.Fields(“DeviceID”).Value & “”””
End If
If shpCurrent.CellExists(“Prop.IPAddress”, 0) Then
shpCurrent.Cells(“Prop.IPAddress”).FormulaU = “””” & rsDevice.Fields(“IPAddress”).Value & “”””
End If
‘ シェイプの一意なIDをキーとしてバインドできるよう、UDPropに保持させるなど応用可能
shpCurrent.NameID = “Dev_” & rsDevice.Fields(“DeviceID”).Value
End If
rsDevice.MoveNext
Loop
rsDevice.Close
‘ — 7. コネクション(接続線)の動的描画ロジック —
Set rsConnection = CreateObject(“ADODB.Recordset”)
rsConnection.Open “SELECT FromDeviceID, ToDeviceID FROM T_Connections”, conn, 1, 1
Dim shpFrom As Visio.Shape
Dim shpTo As Visio.Shape
Dim connLine As Visio.Shape
Do While Not rsConnection.EOF
On Error Resume Next
Set shpFrom = pgActive.Shapes.ItemU(“Dev_” & rsConnection.Fields(“FromDeviceID”).Value)
Set shpTo = pgActive.Shapes.ItemU(“Dev_” & rsConnection.Fields(“ToDeviceID”).Value)
On Error GoTo ErrorHandler
If Not shpFrom Is Nothing And Not shpTo Is Nothing Then
‘ 2つのシェイプ間に動的にコネクタを接続
Set connLine = pgActive.Drop(appVisio.Masters.ItemU(“Dynamic connector”), 0, 0)
connLine.CellsU(“BeginX”).GlueTo shpFrom.CellsU(“PinX”)
connLine.CellsU(“EndX”).GlueTo shpTo.CellsU(“PinX”)
End If
Set shpFrom = Nothing
Set shpTo = Nothing
rsConnection.MoveNext
Loop
rsConnection.Close
‘ — 8. 保存とPDFエクスポート —
docTarget.SaveAs outputVsdxPath
‘ Visioのネイティブ機能による高精度PDF出力 (値: 1 = pdf)
docTarget.ExportAsFixedFormat 1, outputPdfPath, 0, 0
‘ 正常終了のログ出力など(省略)
MsgBox “図面の自動生成が正常に完了しました。”, vbInformation, “成功”
CleanUp:
‘ — 9. 厳格なリソース解放 (Memory Leak Prevention) —
On Error Resume Next
If Not rsDevice Is Nothing Then If rsDevice.State = 1 Then rsDevice.Close
If Not rsConnection Is Nothing Then If rsConnection.State = 1 Then rsConnection.Close
If Not conn Is Nothing Then If conn.State = 1 Then conn.Close
Set rsDevice = Nothing
Set rsConnection = Nothing
Set conn = Nothing
If Not docTarget Is Nothing Then docTarget.Close False
Set docTarget = Nothing
If Not appVisio Is Nothing Then
appVisio.ScreenUpdating = True
appVisio.AlertsEnabled = True
appVisio.Quit
End If
Set appVisio = Nothing
Exit Sub
ErrorHandler:
‘ 異常系ハンドリング
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
Resume CleanUp
End Sub
‘ 簡易的なNull置換ヘルパー関数
Private Function Nz(ByVal varValue As Variant, ByVal defaultValue As Variant) As Variant
If IsNull(varValue) Then
Nz = defaultValue
Else
Nz = varValue
End If
End Function
—
3. チーフアーキテクトが教える実装の急所(Deep Dive)
上記のコードをただ動かすだけではなく、実務の現場でトラブルを起こさないために、以下の3点を心に刻んでおいてほしい。
① 画面描画の完全抑制(`ScreenUpdating = False`)
Visioは、シェイプを1つドロップするたびにキャンバスの再描画走査を行う仕様になっている。数千個のオブジェクトを配置する際、これを有効にしたままだとCPUが張り付き、処理が数十分単位で終わらなくなる。必ず`ScreenUpdating` と `AlertsEnabled` はオフにし、処理の最後に明示的に戻すこと。
② コネクタの糊付け(GlueTo)の作法
コード内で `GlueTo` を用いて動的コネクタを接続しているが、ここで重要なのは接続元・接続先のシェイプが確実に画面上にロードされ、`NameID` またはオブジェクトとして一意に特定できる状態にあることだ。
もしマスターが存在しない、あるいはIDが一致しない場合にエラーで落ちないよう、個別の `On Error Resume Next` と組み合わせたフォールバック構造が必須となる。
③ COMオブジェクトの「徹底的な」解放
VBAにおいて、`Set obj = Nothing` をサボると、見えないところでVisioのプロセス(`Visio.exe`)がメモリ上に常駐し続ける(いわゆるゾンビプロセス問題)。
今回のコードでは、`CleanUp` ラベルを必ず通過するように `On Error GoTo ErrorHandler` と連動させており、どんなに異常な例外が起きたとしても、ADOコネクションとVisioアプリケーションインスタンスが確実にメモリから破棄される設計にしている。
—
4. さらなる高みへ:運用自動化への拡張
このVBAマクロ単体でも十分に実用に耐えるが、実際のエンタープライズ環境では、これをWindowsの「タスクスケジューラ」や「PowerShell」からサイレント実行できるようにラップするのが常套手段だ。
PowerShellからVisioマクロを完全バックグラウンドでキックするスニペット
$Visio = New-Object -ComObject Visio.Application
$Visio.Visible = $false
$Visio.ScreenUpdating = False
$Visio.Documents.Open(“C:\Enterprise\VBA_Holder.vsdm”)
$Visio.Run(“GenerateDiagramFromDatabase”)
$Visio.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Visio) | Out-Null
データ駆動型の図面自動生成は、仕様変更に対する保守性も高く、手動オペレーションによるヒューマンエラーを100%排除できる極めて強力な武器となる。
ぜひ、あなたの現場のパイプラインにもこの設計思想を取り入れてみてほしい。圧倒的な処理速度と堅牢性に驚くはずだ。
