【上級者向け】Visioレイヤー制御の極限:SQL Server連携によるパーソナライズド・ドキュメント自動生成システム
レガシーなCADや図面管理システムにおいて、ユーザーごとに「見せるべき情報」を動的に切り替える要件は、常にアーキテクトを悩ませる課題の一つだ。同一のマスター図面でありながら、インフラエンジニアには配線レイヤーのみを、セキュリティ担当者には入退室管理レイヤーのみを、そして経営層にはサマリーレイヤーのみを表示させる。
これを手動で行う運用は、ヒューマンエラーの温床であり、セキュリティ上のリスクをも孕む。
本稿では、Microsoft VisioのVBA(Visual Basic for Applications)と外部データベース(SQL Server)を極限まで最適化された状態で結合し、ドキュメントオープンと同時にユーザーのロール(権限)に応じたレイヤーの可視化制御をミリ秒単位で完遂するエンタープライズ・アーキテクチャを解説する。
単なるコードの断片ではない。オブジェクトのライフサイクル管理、ADOのメモリリーク対策、そしてVisioの再描画エンジン(Redraw Engine)をハックする実戦的知見を授けよう。
—
1. アーキテクチャの全体像と設計思想
本システムは、以下の3層構造で動作する。
1. データ層 (SQL Server): ユーザーIDと、そのユーザーが所属するロール、および図面(Document GUID)ごとのレイヤー表示状態(Visible / Hidden)を保持する。
2. クライアント実行層 (Visio VBA): `Document_DocumentOpened` イベントをフックし、実行ユーザーのWindows資格情報(またはVisio環境変数)を取得。
3. 通信・制御層 (ADODB & Visio DOM): SQL Serverへ高速クエリを発行し、取得したバイナリ/フラグ情報を基に、Visioの `Layer` オブジェクト群のプロパティを書き換える。
パフォーマンスのボトルネックを排除する極意
VisioのVBAで最もやってはいけないアンチパターンは、「図形の数だけループを回す」「レイヤー変更のたびに画面描画を更新する」ことである。VisioのDOMはCOMを介して動作するため、画面描画(UI Redraw)のコストは非常に高い。
レイヤーの制御を行う際は、必ず明示的に画面更新を停止させ、全処理の完了後に一括解放(フラッシュ)しなければならない。
—
2. データベーススキーマの設計
SQL Server側には、最小限かつ拡張性の高いスキーマを用意する。
— ユーザーごとのレイヤー表示設定テーブル
CREATE TABLE dbo.VisioLayerPermissions (
PermissionID INT IDENTITY(1,1) PRIMARY KEY,
DocumentGUID NVARCHAR(50) NOT NULL,
UserAccount NVARCHAR(100) NOT NULL, — 例: DOMAIN\username
LayerName NVARCHAR(100) NOT NULL,
IsVisible BIT NOT NULL DEFAULT 1,
CONSTRAINT UQ_DocUserLayer UNIQUE (DocumentGUID, UserAccount, LayerName)
);
—
3. 実装コード:Visio VBA モジュール
以下のコードは、エラーハンドリング、ADO接続の確実なクリーンアップ、そしてVisioの画面描画抑制(`ScreenUpdating`)を実装したプロダクション・グレードのものである。
ThisDocument モジュール、または標準モジュールに配置して運用せよ。
Option Explicit
‘ ==============================================================================
‘ チーフアーキテクト特製: SQL Server連携レイヤー制御エンジン
‘ ==============================================================================
Private Const DB_CONNECTION_STRING As String = _
“Provider=MSOLEDBSQL;Server=sql-server.enterprise.local;Database=VisioDB;Trusted_Connection=yes;”
‘ ドキュメントオープン時に自動実行されるイベントハンドラー
Private Sub Document_DocumentOpened(ByVal Doc As IVDocument)
On Error GoTo ErrorHandler
Dim targetUser As String
targetUser = GetCurrentWindowsUser()
‘ 【重要】パフォーマンス最適化: 画面描画とイベントを完全停止
Application.ScreenUpdating = False
Application.EventEnabled = False
‘ レイヤー状態の同期実行
Call SyncLayersFromDatabase(Doc, targetUser)
ErrorHandler:
If Err.Number <> 0 Then
MsgBox “レイヤー同期中に重大なエラーが発生しました: ” & Err.Description, vbCritical, “System Error”
End If
‘ 【重要】リソースの確実な復旧(例外発生時も必ず通す)
Application.ScreenUpdating = True
Application.EventEnabled = True
End Sub
‘ 現在のWindowsログオンユーザーを取得
Private Function GetCurrentWindowsUser() As String
Dim wshNetwork As Object
Set wshNetwork = CreateObject(“WScript.Network”)
GetCurrentWindowsUser = wshNetwork.UserDomain & “\” & wshNetwork.UserName
Set wshNetwork = Nothing
End Function
‘ データベースから設定を取得し、Visioレイヤーに適用するメインロジック
Private Sub SyncLayersFromDatabase(ByVal Doc As IVDocument, ByVal userName As String)
Dim conn As Object
Dim cmd As Object
Dim rs As Object
Dim docGuid As String
docGuid = Doc.DocumentSheet.Cells(“TheText”).ResultStr(“”) ‘ または独自メタデータを使用
‘ ※簡易的に固定値、あるいはファイル名を用いる場合は Doc.Name を使用
docGuid = Doc.Name
‘ ADOオブジェクトのインスタンス化
Set conn = CreateObject(“ADODB.Connection”)
Set cmd = CreateObject(“ADODB.Command”)
conn.ConnectionString = DB_CONNECTION_STRING
conn.CursorLocation = 3 ‘ adUseClient (メモリ上でのカーソル処理で高速化)
conn.Open
Set cmd.ActiveConnection = conn
cmd.CommandType = 1 ‘ adCmdText
cmd.CommandText = “SELECT LayerName, IsVisible FROM dbo.VisioLayerPermissions WHERE DocumentGUID = ? AND UserAccount = ?”
‘ プレースホルダーによるSQLインジェクション対策
cmd.Parameters.Append cmd.CreateParameter(“DocGUID”, 200, 1, 50, docGuid)
cmd.Parameters.Append cmd.CreateParameter(“UserAcc”, 200, 1, 100, userName)
Set rs = cmd.Execute
If Not (rs.BOF And rs.EOF) Then
Dim vPage As IVPage
Dim vLayer As IVLayer
Dim layerName As String
Dim isVisible As Boolean
‘ ドキュメント内の全ページ・全レイヤーを走査
Dim pIdx As Long, lIdx As Long
For pIdx = 1 To Doc.Pages.Count
Set vPage = Doc.Pages(pIdx)
For lIdx = 1 To vPage.Layers.Count
Set vLayer = vPage.Layers(lIdx)
layerName = vLayer.Name
‘ レコードセットを先頭からスキャン(小規模データセット前提のためループ処理)
rs.MoveFirst
Do While Not rs.EOF
If StrComp(rs.Fields(“LayerName”).Value, layerName, vbTextCompare) = 0 Then
isVisible = CBool(rs.Fields(“IsVisible”).Value)
‘ レイヤーの表示状態制御 (visLayerVisible = 1, 非表示の場合はマスク処理)
‘ 厳密には CellsU(“Layer.Visible”) を操作する
If isVisible Then
vLayer.CellsC(visLayerVisible).FormulaU = “TRUE”
Else
vLayer.CellsC(visLayerVisible).FormulaU = “FALSE”
End If
Exit Do
End If
rs.MoveNext
Loop
Next lIdx
Next pIdx
End If
CleanUp:
‘ 【オブジェクトの明示的解放】メモリリークの完全阻止
On Error Resume Next
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
If Not cmd Is Nothing Then Set cmd = Nothing
If Not conn Is Nothing Then conn.Close: Set conn = Nothing
On Error GoTo 0
End Sub
—
4. チーフアーキテクトが教える「現場の知見と罠」
このシステムを社内展開するにあたり、幾多の現場で遭遇した「落とし穴」と、その対策を共有する。
1. COMオブジェクトの参照リーク(Memory Leak)
VBAのガベージコレクションは非常に緩慢である。特に `ADODB.Connection` や `Recordset` を多用するスクリプトでは、`Set obj = Nothing` を怠ると、Visioプロセスがメモリを食潰し、やがてクラッシュする。
上記のコード例のように、`CleanUp` ラベルを必ず用意し、エラー発生有無に関わらずオブジェクトを破棄(`Nothing`代入)する防御的プログラミングを徹底せよ。
2. Visioのセキュリティセンター(マクロの有効化)
エンタープライズ環境において、ネットワーク共有フォルダやSQLから取得したVSDMファイルは、デフォルトで「マクロが無効(信頼できない場所)」として開かれる。
これをクリアするためには、グループポリシー(GPO)を用いて、社内SQL Serverと連携するドキュメント格納フォルダを「信頼できる場所(Trusted Locations)」としてクライアントPC群に強制配布することが必須条件となる。
3. レイヤー名の言語依存性(Localization)
Visioのデフォルトレイヤー(例: “Background”, “Connector” 等)は、インストールされているOfficeの言語(日本語/英語)によって名称が動的に変化する。
多言語環境が混在するグローバル企業においては、レイヤー名でSQLを引くのではなく、ユーザー定義セルやレイヤーのインデックス(`Index`)、あるいはカスタムで付与したGUIDをキーにする設計に昇華させるべきだ。
—
5. 結びにかえて
VBAは、しばしば「レガシーな言語」と揶揄される。しかし、それは使い手の技量が低い場合の言い訳に過ぎない。
Windows APIやADO、そしてVisioの内部DOM構造を熟知したエンジニアが書いたVBAコードは、モダンなデスクトップアプリケーションと遜色ない速度と堅牢性で動作する。
本稿で示したSQL Server連携レイヤー制御システムは、単なる自動化の枠を超え、「図面そのものが文脈を理解し、見る者を選ぶ」という次世代のドキュメント・ガバナンスを実現する。
あなたの現場でも、この知見を直ちに実装し、手動運用の泥沼からシステムを解放してほしい。
