【実務・中級編】【上級者】PowerPointのアプリケーションイベント(PresentationOpen / BeforeSave)をクラスモジュールで完全フックし、全操作のログをリアルタイムで中央データベースに非同期送信する監査証跡アドイン – PowerPoint VBA解析バイブル

スポンサーリンク
スポンサーリンク

PowerPoint VBAを掌握する極限の知見:アプリケーションイベントを完全フックする「監査証跡アドイン」の設計思想

「誰が、いつ、どの機密ファイルを操作したか」

エンタープライズ・レベルのセキュリティ要件において、この問いに対する回答は「推測」ではなく「確実なログ」でなければならない。しかし、多くの開発者は誤ったアプローチを取る。個別のプレゼンテーションファイルにマクロを埋め込むような、分散型の、そして極めて脆い設計だ。

もし、あるユーザーがマクロを無効化したり、ファイルをコピーしてマクロを削除したりしたらどうなる? ログの連鎖はそこで断絶する。これは「自動化」ではなく、単なる「おまけ」に過ぎない。

真のアーキテクトが構築すべきは、PowerPointというアプリケーションそのものに寄生し、全ての動作を監視する「アドイン型監査エンジン」である。本稿では、クラスモジュールを用いたアプリケーションイベントの完全フックと、非同期的なデータ送信を模した堅牢な設計手法を伝授する。

1. アーキテクチャの核心:なぜクラスモジュールなのか

PowerPointのイベント(ファイルの起動、保存、クローズ等)を捕捉するためには、`Application` オブジェクトのプロパティを `WithEvents` キーワードを用いて宣言する必要がある。

ここで重要なのは、イベントを捕捉するオブジェクトのライフサイクルを制御することだ。標準モジュールに直接イベントを書くことはできない。必ずクラスモジュール内にインスタンスを保持し、そのインスタンスをグローバル変数としてアプリケーションの起動期間中、メモリ上に生存させ続けなければならない。

設計上の致命的なミス:同期処理によるユーザー体験の破壊

多くのジュニアエンジニアは、`BeforeSave` イベント内で直接データベース(SQL Server等)へ接続し、`INSERT` 文を発行しようとする。これは絶対にしてはならない設計だ。

VBAはシングルスレッドである。DB接続のタイムアウトやネットワークの遅延が発生した瞬間、ユーザーの「保存」操作はフリーズする。ユーザーは「ソフトが壊れた」と感じ、業務効率は著しく低下する。

我々が取るべき戦略は、直接的なDB接続ではなく、軽量なWeb API(REST API)へのHTTP POST送信である。 これにより、VBA側の処理負荷を最小限に抑え、セキュリティ(DB接続情報の隠蔽)とパフォーマンスを両立させる。

2. 実装:監査証跡アドインのプロトタイプ

以下に、プロダクション環境に耐えうる設計思想に基づいたコードを示す。

A. クラスモジュール:`clsAppEventMonitor`

このクラスが、アプリケーションの「神経系」となる。

Option Explicit

' Applicationオブジェクトのイベントを捕捉するための宣言
Private WithEvents App As Application

' 監査ログ送信用のAPIエンドポイント(環境に合わせて変更)
Private Const API_URL As String = "https://api.your-company.com/v1/audit-log"

''' <summary>
''' モニターの初期化
''' </summary>
''' <param name="appInstance">PowerPoint Applicationインスタンス</param>
Public Sub Initialize(ByVal appInstance As Application)
    Set App = appInstance
End Sub

''' <summary>
''' プレゼンテーションが開かれた時のイベント
''' </summary>
Private Sub App_PresentationOpen(ByVal Pres As Presentation)
    On Error GoTo ErrorHandler
    Call LogEvent("PresentationOpen", Pres.Name)
    Exit Sub
ErrorHandler:
    ' ログ送信の失敗が、ユーザーのメイン操作(ファイルを開く)を阻害してはならない
    Debug.Print "Audit Log Error (Open): " & Err.Description
End Sub

''' <summary>
''' 保存される直前のイベント
''' </summary>
Private Sub App_BeforeSave(ByVal Pres As Presentation, Cancel As Boolean)
    On Error GoTo ErrorHandler
    ' 保存処理自体をキャンセルさせることはしない。あくまで「記録」に徹する。
    Call LogEvent("BeforeSave", Pres.Name)
    Exit Sub
ErrorHandler:
    Debug.Print "Audit Log Error (Save): " & Err.Description
End Sub

''' <summary>
''' ログデータをAPIへ送信する(擬似非同期的なアプローチ)
''' </summary>
Private Sub LogEvent(ByVal eventType As String, ByVal fileName As String)
    Dim http As Object
    Dim payload As String
    Dim userName As String
    
    ' コンテキスト情報の収集
    userName = Environ("USERNAME")
    
    ' JSONペイロードの構築(簡易的な文字列結合)
    ' 本来はJSONライブラリを使用すべきだが、依存関係を減らすためここでは文字列操作で行う
    payload = "{""event"":""" & eventType & """,""file"":""" & fileName & """,""user"":""" & userName & """,""timestamp"":""" & Now & """}"
    
    ' MSXML2.XMLHTTP を使用してAPIへPOST
    ' タイムアウト設定を短くすることで、ネットワーク遅延によるフリーズを最小化する
    Set http = CreateObject("MSXML2.XMLHTTP")
    
    On Error Resume Next ' ネットワークエラーによるクラッシュを防止
    http.Open "POST", API_URL, False ' 第3引数をFalseにすると同期通信になるが、API側を高速に設計することで体感差を無くす
    http.setRequestHeader "Content-Type", "application/json"
    http.Send payload
    
    If Err.Number <> 0 Then
        ' ログ送信失敗時は、ローカルのテキストファイルにバックアップを出す等のフォールバックを検討せよ
        Call WriteFallbackLog(payload)
    End If
    On Error GoTo 0
    
    Set http = Nothing
End Sub

''' <summary>
''' API送信失敗時のフォールバック処理
''' </summary>
Private Sub WriteFallbackLog(ByVal data As String)
    Dim fso As Object
    Dim logFile As Object
    Dim path As String
    
    path = Environ("TEMP") & "\ppt_audit_fallback.log"
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set logFile = fso.OpenTextFile(path, 8, True) ' 8 = ForAppending
    logFile.WriteLine data
    logFile.Close
End Sub

B. 標準モジュール:`modMain`

アドインの起動時に、クラスのインスタンスを生成・保持する。

Option Explicit

' クラスのインスタンスをグローバルに保持し、ライフサイクルを維持する
Private Monitor As clsAppEventMonitor

''' <summary>
''' アドイン起動時に呼び出されるエントリーポイント
''' </summary>
Public Sub Auto_Open()
    ' PowerPoint起動時に自動実行される
    Call StartMonitoring
End Sub

''' <summary>
''' モニタリングを開始する
''' </summary>
Public Sub StartMonitoring()
    On Error GoTo ErrorHandler
    
    ' 既にインスタンスが存在する場合は一度破棄(二重起動防止)
    Set Monitor = Nothing
    
    ' クラスのインスタンス化
    Set Monitor = New clsAppEventMonitor
    
    ' Applicationオブジェクトを注入
    Monitor.Initialize Application
    
    Debug.Print "Audit Monitor Started Successfully."
    Exit Sub

ErrorHandler:
    MsgBox "Failed to initialize Audit Monitor. Please contact IT support.", vbCritical
End Sub

3. プロダクション実装における「極限の注意点」

このツールを実務に投入するにあたり、以下の3点は設計書に必ず明記せよ。

① エラーハンドリングの「分離」

`On Error GoTo ErrorHandler` を書くだけでは不十分だ。重要なのは、「監視側のエラーが、ユーザーの本来の業務(保存や開く)を中断させてはならない」という原則である。`LogEvent` 内のすべての処理は、万が一失敗しても `Application` の動作に影響を与えないよう、極めて防御的に記述しなければならない。

② セキュリティ:API認証の設計

VBAコード内にAPIキーをハードコードするのは素人のすることだ。
アドインを配布する場合、API側で「特定の社内IP帯域からのリクエストのみ許可する」か、あるいは「Windows認証(NTLM/Kerberos)を利用したエンドポイント」を用意することを強く推奨する。これにより、VBA側での認証ロジックを排除し、堅牢性を高められる。

③ パフォーマンス:JSONの構築

本例では文字列結合でJSONを生成しているが、複雑なデータ構造を扱う場合は、専用のJSONライブラリ(`VBA-JSON`など)を組み込むべきだ。ただし、アドインとしての軽量性を維持するため、依存ライブラリは最小限に留めるのがアーキテクトの美学である。

結論

PowerPoint VBAを用いた高度な監査ツールの構築は、単なるコードの記述ではない。それは、「ユーザーの利便性」と「組織の統制」という、時に相反する要求を、オブジェクトのライフサイクルと通信プロトコルの理解によって調和させる設計プロセスである。

この設計思想を理解し、実装に落とし込めるようになれば、君は単なる「自動化担当者」を脱し、真の「システムアーキテクト」への一歩を踏み出すことになるだろう。

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