「記録」から「制御」へ。Visio VBAで「見えない守護神(監査ログ)」を構築する技術
こんにちは。Visio VBAの深淵へようこそ。
皆さんは、マクロの記録機能を使って「あ、これ自動化できるな」と感じたことはありませんか?
もちろん、マクロの記録は素晴らしい第一歩です。しかし、そこから一歩踏み出し、「ユーザーが何かをした瞬間に、システムが自動で反応する」という領域に足を踏み入れたとき、あなたのエンジニアとしての視界は劇的に開けます。
今日は、単なる作業の自動化を超えた、「システムの監視と監査」という高度なテーマに挑戦しましょう。
テーマは、「ページが追加・削除された瞬間に、その履歴をテキストファイルに自動記録する監査ログシステム」です。
これを作る過程で、VBAの最重要概念の一つである「クラスモジュールによるイベントハンドリング」と、初心者が必ず躓く「オブジェクトの生存期間(ライフサイクル)」の本質をマスターしていただきます。
ここをクリアすれば、あなたは「ツールを作る人」から「システムを設計する人」へと進化できるはずですよ。
1. なぜ「普通のモジュール」ではいけないのか?
まず、設計思想の話をしましょう。
通常のマクロ(標準モジュール)は、あなたが「実行ボタン」を押したときにだけ動きます。いわば、「命令待ちの作業員」です。
しかし、今回やりたいことは違います。
「ユーザーが勝手にページを消した」「新しいページを作った」という、ユーザーの動作(イベント)をリアルタイムで検知したいのです。
これを実現するには、常にVisioの動きを背後で見守り続ける「監視役」が必要です。この「監視役」の役割を担えるのが、クラスモジュールなのです。
仕組みのイメージ図
[ ユーザーの操作 ]
↓ (ページ追加!)
[ Visio Application ]
↓ (イベント発生!)
[ クラスモジュール (監視役) ] ← ここが常に待ち構えている
↓ (検知したぞ!)
[ ログ出力関数 ] → [ audit_log.txt ]
2. 実装:監査ログシステムの構築
それでは、実際にコードを書いていきましょう。
作業は3つのステップに分かれます。
1. クラスモジュールの作成(監視役の定義)
2. 標準モジュールの作成(監視役の起動と停止)
3. ログ出力の実装(記録の実行)
ステップ1:クラスモジュールの作成
まず、VBAエディタのメニューから `挿入` -> `クラスモジュール` を選択してください。
作成されたクラスの名前を、プロパティウィンドウから `clsVisioWatcher` に変更してください。ここが重要です。
' --- クラスモジュール名: clsVisioWatcher ---
Option Explicit
' WithEvents を使うことで、Applicationのイベントを「捕まえる」ことができるようになります
' これが「監視役」の心臓部です
Private WithEvents App As Visio.Application
' 監視を開始するためのメソッド
Public Sub Initialize(ByVal visioApp As Visio.Application)
Set App = visioApp
Debug.Print "監査ログシステム:監視を開始しました。"
End Sub
' 監視を停止するためのメソッド
Public Sub Terminate()
Set App = Nothing
Debug.Print "監査ログシステム:監視を終了しました。"
End Sub
' --- イベントハンドラ:ページが追加されたとき ---
Private Sub App_PageAdd(ByVal Page As Visio.Page)
Call WriteLog("PAGE_ADD", "新しいページが追加されました: " & Page.Name)
End Sub
' --- イベントハンドラ:ページが削除されたとき ---
Private Sub App_PageDelete(ByVal Page As Visio.Page)
' 注意:削除された直後のオブジェクトにアクセスするとエラーになることがあるため、
' 名前などの情報をあらかじめ取得しておく設計が理想的ですが、ここではシンプルに記述します
Call WriteLog("PAGE_DELETE", "ページが削除されました。")
End Sub
' --- ログ書き出しの内部処理 ---
Private Sub WriteLog(ByVal action As String, ByVal details As String)
Dim logPath As String
Dim fileNum As Integer
' ログファイルの保存先(このExcel/Visioファイルと同じフォルダ)
logPath = ThisDocument.Path & "\visio_audit_log.txt"
fileNum = FreeFile
' ファイルを追記モード(Append)で開く
Open logPath For Append As #fileNum
' [日時] [アクション] 詳細 という形式で記録
Print #fileNum, "[" & Now & "] [" & action & "] " & details
Close #fileNum
' イミディエイトウィンドウにも表示(デバッグ用)
Debug.Print "Log Recorded: " & action
End Sub
ステップ2:標準モジュールの作成
次に、`挿入` -> `標準モジュール` を選択し、以下のコードを記述します。
ここで最も重要なテクニックは、「監視役(クラスのインスタンス)をモジュールレベル変数として保持する」ことです。
' --- 標準モジュール ---
Option Explicit
' 【超重要】ここを Sub の中ではなく、モジュールの先頭(宣言セクション)に書くこと!
' これにより、マクロが終わっても「監視役」がメモリ上に生き残り続けます。
Private Watcher As clsVisioWatcher
' 監視システムを起動するマクロ
Sub StartAuditSystem()
' 監視役(クラス)のインスタンスを作成
Set Watcher = New clsVisioWatcher
' Visioアプリケーションを監視役にセット
Watcher.Initialize Visio.Application
MsgBox "監査ログシステムを起動しました。" & vbCrLf & _
"ページの追加・削除を記録します。", vbInformation
End Sub
' 監視システムを停止するマクロ
Sub StopAuditSystem()
If Not Watcher Is Nothing Then
Watcher.Terminate
Set Watcher = Nothing
MsgBox "監査ログシステムを停止しました。", vbExclamation
Else
MsgBox "システムは起動していません。", vbInformation
End If
End Sub
3. ここがプロの分かれ道:陥りやすいエラーと「生存期間」
ここまでのコードを動かすと、素晴らしい体験ができるはずです。しかし、初心者が必ずと言っていいほど陥る「罠」があります。
罠:なぜかログが記録されない!?
もし、`StartAuditSystem` の中で `Dim Watcher As clsVisioWatcher` と宣言してしまったらどうなるでしょうか?
' 失敗例
Sub StartAuditSystem()
Dim Watcher As clsVisioWatcher ' プロシージャ内のローカル変数
Set Watcher = New clsVisioWatcher
Watcher.Initialize Visio.Application
End Sub ' ← ここで Watcher はメモリから消滅(死)してしまう!
プロシージャ(Sub)の中で宣言された変数は、そのSubが終わった瞬間に破棄されます。
つまり、監視役を起動した瞬間に、監視役が消えてしまうのです。
「オブジェクトのライフサイクル(生存期間)」を制御すること。
これが、単なるスクリプト作成者と、システムエンジニアを分ける決定的な差です。クラスをモジュールレベル(プロシージャの外)で宣言することで、私たちは「システムが動いている間、ずっと監視役を生かしておく」ことができるのです。
4. まとめ:次の一歩へ
お疲れ様でした!
今回作成したシステムは、以下の3つの高度な概念を組み合わせています。
1. クラスモジュール:イベントを待ち受ける「オブジェクト」の設計。
2. WithEvents:アプリケーションの挙動をフックする「魔法のキーワード」。
3. ライフサイクル管理:変数のスコープを理解し、システムを常駐させる「設計思想」。
この仕組みを応用すれば、「図形が変更されたら記録する」「特定のレイヤーが操作されたら警告を出す」といった、より複雑な自動化システムへと拡張できます。
「マクロの記録」という殻を破り、Visioというプラットフォームの上で、独自の「動的なシステム」を構築する楽しさを、ぜひ味わってください。
ここをクリアすれば、Visio VBAの基本はバッチリですよ。
さあ、次はどんなシステムを作りましょうか?
*Happy Coding!*
