Visio VBAの深淵:SQL Server連動による「動的図面生成パイプライン」の構築
エンジニア諸君。Visioのオートメーションにおいて、「コードが動く」ことと「システムが死なない」ことの間には、深い溝がある。
多くの者がDAOやADOを叩き、`Application.Documents.Open` を繰り返してメモリをリークさせ、結果としてVisioのプロセスを肥大化させ、最終的にハングアップする。これはプロフェッショナルの仕事ではない。
今回は、エンタープライズの現場で「止まらない、汚さない、速い」自動化を実現するための、Visio図面生成アーキテクチャの核心を伝授する。
—
1. アーキテクチャの鉄則:プロセス分離とリソース管理
VisioのCOMオブジェクトは極めて脆い。特にVSDX形式は内部がXML(OPCパッケージ)であるため、不適切なインスタンス生成はメモリ解放の遅延を招く。
核心となる設計指針
- Late Binding(遅延バインディング)の回避: `Object`型での宣言は、開発時の利便性と引き換えに実行時の型チェックコストを支払う。`Microsoft Visio 16.0 Type Library` を参照し、明示的に型を定義せよ。
- Object Lifecycleの制御: ループ内で`Set obj = Nothing`を呼ぶだけでは不十分だ。`.Close`メソッドの後に明示的なガベージコレクションを意識した参照解放を行う。
- DAO/ADOの分離: データベース接続ロジックをVBAのモジュールに直接書くな。可能であればVB.NETでCOMラッパーDLLを作成し、VBAから呼び出すのが「エンタープライズ」の流儀だ。
—
2. 実装:SQL Server連動・動的生成パイプライン
以下は、テンプレートをベースにSQL Serverから取得したノード情報を配置する、堅牢な生成プロセスの雛形である。
Option Explicit
‘ メモリ管理の定石:グローバルスコープでのオブジェクト保持を避ける
Public Sub GenerateDiagramFromSQL()
Dim vApp As Visio.Application
Dim vDoc As Visio.Document
Dim vPage As Visio.Page
Dim conn As Object ‘ ADODB.Connection
Dim rs As Object ‘ ADODB.Recordset
‘ プロセス起動の最適化(画面更新を停止)
Set vApp = New Visio.Application
vApp.ScreenUpdating = False
vApp.AlertResponse = 7 ‘ ダイアログを自動で「いいえ」または非表示に
On Error GoTo Cleanup
‘ データベース接続(接続文字列は環境変数/外部ファイルから読み込むこと)
Set conn = CreateObject(“ADODB.Connection”)
conn.Open “Provider=SQLOLEDB;Data Source=SERVER;Initial Catalog=DB;Integrated Security=SSPI;”
Set rs = conn.Execute(“SELECT ShapeID, X, Y, Label FROM Dim_Shapes”)
‘ テンプレートから新規作成
Set vDoc = vApp.Documents.Add(“C:\Templates\BaseTemplate.vstx”)
Set vPage = vDoc.Pages(1)
‘ シェイプ配置ループ
Do While Not rs.EOF
Call PlaceShape(vPage, rs!X, rs!Y, rs!Label)
rs.MoveNext
Loop
‘ 保存とPDF出力
vDoc.SaveAs “C:\Output\Result_” & Format(Now, “yyyymmdd_hhnn”) & “.vsdx”
vDoc.Export “C:\Output\Result.pdf”
Cleanup:
‘ 明示的な終了処理
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
If Not conn Is Nothing Then conn.Close: Set conn = Nothing
If Not vDoc Is Nothing Then vDoc.Close False: Set vDoc = Nothing
vApp.ScreenUpdating = True
vApp.Quit
Set vApp = Nothing
If Err.Number <> 0 Then MsgBox “Error: ” & Err.Description, vbCritical
End Sub
Private Sub PlaceShape(page As Visio.Page, x As Double, y As Double, label As String)
‘ テンプレート内のマスターシェイプをドロップ
Dim shp As Visio.Shape
Set shp = page.Drop(page.Application.Documents(“Stencil.vssx”).Masters(“Node”), x, y)
shp.Text = label
End Sub
—
3. レガシー環境を生き抜くための極限の知見
Windows APIによる「強制終了」の回避
Visioがフリーズした際、タスクマネージャーに頼るな。`user32.dll` の `GetWindowThreadProcessId` を使い、特定の図面ウィンドウだけをPIDから特定して終了させる設計を組み込むのが、真の管理者だ。
PDF出力の罠
`Export`メソッドは、時としてVisioのレンダリングエンジンと競合する。大規模な図面の場合、一度`.SaveAs`でVSDXを確定させてから、`PrintOut`メソッドで「Microsoft Print to PDF」へリダイレクトする手法の方が、カラープロファイルの崩れやフォント欠けを回避できる確率は高い。
パフォーマンスを極める
シェイプが数千個を超える場合、`Drop`メソッドは遅すぎる。
1. グループ化: シェイプを個別にDropせず、XML構造を直接書き換えるアプローチ(VSDXの裏側を直接操作する)も選択肢に入れよ。
2. イベント抑制: `vApp.EventEnabled = False` を活用し、シェイプ配置ごとの再描画や計算をすべて停止させること。これだけで処理速度は3倍以上変わる。
—
結びに代えて
自動化とは、単に手作業をコードに置き換えることではない。「システムが本来持っているはずの安定した挙動を、エンジニアが管理下に置くこと」である。
もし君がこのコードをコピー&ペーストするだけでなく、なぜ`ScreenUpdating`を落とすのか、なぜ`Nothing`代入が不可欠なのかを理解したなら、君もまた、レガシーという名の戦場で生き残る資格がある。
次の設計では、この「パイプライン」をさらに拡張し、非同期処理によるキューイングまで視野に入れてみてほしい。健闘を祈る。
