【テクニカル・上級編】【上級者向け】レイヤーの表示・非表示状態をSQL Serverに保存し、ユーザーごとに図面表示をカスタマイズする連携術 – Visio VBA解析バイブル

スポンサーリンク

【上級者向け】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連携レイヤー制御システムは、単なる自動化の枠を超え、「図面そのものが文脈を理解し、見る者を選ぶ」という次世代のドキュメント・ガバナンスを実現する。
あなたの現場でも、この知見を直ちに実装し、手動運用の泥沼からシステムを解放してほしい。

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