【Visio VBA×SQL Server】ユーザーごとに図面表示を支配する!レイヤー動的制御システムの極意
開発プロジェクトのリーダーである私たちが、現場で最も頭を悩ませる問題の一つが「情報過多の図面」だ。
設備図、ネットワーク図、プラント配管図――。あらゆる情報を1枚のVisio(VSDX)に詰め込んだ結果、現場の担当者は「自分に関係のないレイヤー」の海に溺れ、本来見るべき重要情報を見落とす。
「営業部員には売上関連レイヤーだけを」「保守エンジニアにはインフラ配管レイヤーだけを」
これを人間の手動操作に頼るなど、業務自動化エンジニアの恥だ。
今回は、Visioを開いた瞬間にSQL Serverからユーザーの権限・役割を取得し、レイヤーの表示・非表示を完全自動でパーソナライズする極限の連携術を伝授する。
中途半端なマニュアルの引き写しではない。プロダクション環境で耐えうる、堅牢でセキュアな設計のすべてを明かそう。
—
1. なぜ「力技のVBA」では破綻するのか?(設計思想の破壊と再構築)
多くの初学者がやりがちなのは、Visio起動時イベント(`DocumentOpened`)にハードコードされたSQLをベタ書きし、エラーハンドリングすら怠る実装だ。
これでは、ネットワークの瞬断やDBのスキーマ変更で図面全体がクラッシュする。また、Visioのレイヤーオブジェクト(`Layer`)のライフサイクルを理解していないと、メモリリークや「Shape外れによる予期せぬ例外」を引き起こす。
本アーキテクチャの要件は以下の3点だ:
1. 疎結合の維持: Visioファイル自体には重いロジックを持たせず、外部DBの状態に忠実に追従させる。
2. 高速なADO接続: COMオブジェクトのメモリ解放(Garbage Collectionの明示的制御)を徹底し、Visioの動作を重くしない。
3. フェイルセーフ: DB接続に失敗した場合でも、図面閲覧の業務が停止しないフォールバック機構。
—
2. データベース側の最低限のスキーマ設計
連携の土台として、SQL Server側に最小限のテーブルを用意する。複雑なORMは不要。生SQLの速度と確実性を取れ。
— ユーザーごとの役割定義
CREATE TABLE AppUsers (
UserName NVARCHAR(50) PRIMARY KEY,
UserRole NVARCHAR(50) NOT NULL
);
— 役割ごとのレイヤー表示制御
CREATE TABLE RoleLayerPermissions (
UserRole NVARCHAR(50),
PageName NVARCHAR(50),
LayerName NVARCHAR(50),
IsVisible BIT,
PRIMARY KEY (UserRole, PageName, LayerName)
);
—
3. 【プロダクションコード】VBA実装の極み
以下のコードをVisio図面(またはアドイン)の `ThisDocument` モジュールに実装する。
ADOを用いたSQL Serverへの接続、環境変数(またはWindowsログオン名)からのユーザー特定、そしてVisioのレイヤープロパティの安全な書き換えをすべて網羅している。
Option Explicit
‘ ==============================================================================
‘ 処理名 : ThisDocument.DocumentOpened
‘ 概要 : ドキュメントオープン時にSQL Serverからレイヤー権限を取得し適用する
‘ ==============================================================================
Private Sub Document_DocumentOpened(ByVal Doc As IVDocument)
On Error GoTo ErrorHandler
Dim currentUser As String
Dim userRole As String
‘ 1. 現在のWindowsログオンユーザーを取得(必要に応じてアプリ固有の認証に置き換え)
currentUser = GetWindowsUser()
‘ 2. SQL Serverからユーザーのロールを取得
userRole = GetUserRoleFromDB(currentUser)
If userRole = “” Then
MsgBox “権限情報が取得できませんでした。デフォルト表示で継続します。”, vbExclamation, “レイヤー制御”
Exit Sub
End If
‘ 3. 図面内のページとレイヤーを走査し、DB設定に基づいて表示・非表示を適用
Call ApplyLayerPermissions(Doc, userRole)
Exit Sub
ErrorHandler:
‘ フェイルセーフ:DB連携が失敗してもVisio自体のオープンを阻害しない
MsgBox “レイヤー自動制御システムでエラーが発生しました: ” & Err.Description, vbCritical, “致命的ではないエラー”
End Sub
‘ ——————————————————————————
‘ 補助関数: Windowsユーザー名の取得
‘ ——————————————————————————
Private Function GetWindowsUser() As String
GetWindowsUser = Environ$(“UserName”)
End Function
‘ ——————————————————————————
‘ データベースからロールを取得する関数 (ADO使用)
‘ ——————————————————————————
Private Function GetUserRoleFromDB(ByVal userName As String) As String
Dim conn As Object
Dim cmd As Object
Dim rs As Object
Dim connStr As String
‘ 接続文字列(環境に合わせてIPやDB名、認証方式を変更すること)
connStr = “Provider=MSOLEDBSQL;Server=your_server\SQLEXPRESS;Database=VisioControlDB;Trusted_Connection=yes;”
Set conn = CreateObject(“ADODB.Connection”)
Set cmd = CreateObject(“ADODB.Command”)
conn.Open connStr
Set cmd.ActiveConnection = conn
cmd.CommandText = “SELECT UserRole FROM AppUsers WHERE UserName = ?”
cmd.CommandType = 1 ‘ adCmdText
‘ パラメータ化クエリによるSQLインジェクション対策(上級者なら常識)
cmd.Parameters.Append cmd.CreateParameter(“@UserName”, 200, 1, 50, userName) ‘ adVarChar
Set rs = cmd.Execute
If Not rs.EOF Then
GetUserRoleFromDB = rs.Fields(“UserRole”).Value
Else
GetUserRoleFromDB = “DefaultUser” ‘ フォールバックロール
End If
‘ クリーンアップ(COMオブジェクトの即座な解放がVBAの命綱)
rs.Close
conn.Close
Set rs = Nothing
Set cmd = Nothing
Set N = Nothing
End Function
‘ ——————————————————————————
‘ レイヤー権限をVisioの各ページに適用するメインエンジン
‘ ——————————————————————————
Private Sub ApplyLayerPermissions(ByVal docObj As IVDocument, ByVal role As String)
Dim conn As Object
Dim rs As Object
Dim connStr As String
Dim pg As IVPage
Dim lyr As IVLayer
Dim strSQL As String
connStr = “Provider=MSOLEDBSQL;Server=your_server\SQLEXPRESS;Database=VisioControlDB;Trusted_Connection=yes;”
Set conn = CreateObject(“ADODB.Connection”)
conn.Open
‘ トランザクション・パフォーマンス配慮のため、画面描画を停止
Application.ScreenUpdating = False
For Each pg In docObj.Pages
‘ 各ページに存在するレイヤーの表示状態をDBから取得して制御
strSQL = “SELECT LayerName, IsVisible FROM RoleLayerPermissions WHERE UserRole = ‘” & role & “‘ AND PageName = ‘” & pg.Name & “‘”
Set rs = conn.Execute(strSQL)
Do While Not rs.EOF
Dim targetLayerName As String
Dim shouldBeVisible As Boolean
targetLayerName = rs.Fields(“LayerName”).Value
shouldBeVisible = CBool(rs.Fields(“IsVisible”).Value)
‘ 該当レイヤーをページ内から探索
On Error Resume Next
Set lyr = pg.Layers.Item(targetLayerName)
On Error GoTo 0
If Not lyr Is Nothing Then
‘ Visioレイヤーの可視性制御
‘ visLayerVisible = 1 (通常はビューステートのCells[LayerVisible]を操作するのが堅牢)
If shouldBeVisible Then
lyr.CellsC(visLayerVisible).FormulaU = “TRUE”
Else
lyr.CellsC(visLayerVisible).FormulaU = “FALSE”
End If
End If
Set lyr = Nothing
rs.MoveNext
Loop
rs.Close
.Next pg
Application.ScreenUpdating = True
conn.Close
Set rs = Nothing
Set conn = Nothing
End Sub
—
4. チーフアーキテクトが教える実装の急所(罠と回避策)
このコードを実務に投入する際、以下のポイントを怠ると必ず障害が発生する。
1. COMオブジェクトのメモリリーク(最重要)
VBAの `CreateObject` で生成した ADODB 関連のオブジェクトは、スコープを抜けても即座にメモリから消えないことがある。必ず `Set rs = Nothing`、`Set conn = Nothing` を明示し、オブジェクトの参照カウンタをゼロに落とせ。これをサボると、Visioを何枚も開いた瞬間にメモリ消費量が跳ね上がり、最悪の場合PCがフリーズする。
2. Visioの「PageName」と「LayerName」の完全一致
SQL Server側のマスタデータと、Visio図面上のページ名・レイヤー名が1文字でも異なると設定が適用されない。運用フェーズでは、図面側の名称変更を厳格に制限するか、Visio側から逆引きして動的にテーブル側を同期させる管理スクリプトを別途用意すべきだ。
3. 画面描画の凍結(`ScreenUpdating = False`)
レイヤーの表示・非表示をページごとに切り替える処理は、Visioの再描画コストが非常に高い。ループ内で画面がチラつかないよう、必ず処理の冒頭で描画を止め、最後に一気に復元させろ。これだけで実行速度が数倍〜十倍変わる。
—
5. おわりに:真の業務自動化とは「ユーザーに意識させないこと」
優れたシステムとは、ユーザーがその存在にすら気づかないものだ。
担当者が朝、自分の部署の図面を開いた瞬間、余計なノイズは消え去り、必要な情報だけが美しく描画されている――。今回のSQL Server連携レイヤー制御は、単なるマクロの域を超え、組織全体の情報リテラシーとセキュリティの底上げを実現する強力な武器となる。
あなたのデスクトップにあるその野暮ったいVisioファイルを、今日から「生きた、賢いシステム」へと進化させてほしい。
