【テクニカル・上級編】【プロフェッショナル】アドイン不要の「自己完結型」イベントハンドラ:特定のプレゼンテーション(.pptm)起動時に自動でグローバルイベント監視を開始する実装パターン – PowerPoint VBA解析バイブル

スポンサーリンク

【プロフェッショナル】アドイン不要の「自己完結型」イベント監視:PowerPoint VBAの境界を突破する実装術

現場において「アドイン(.ppam)を全PCに配布せよ」という要求は、情シス部門の管理コストという名の壁に阻まれ、頓挫するのがオチだ。我々が求めているのは、配布の手間を排し、開いた瞬間に自律駆動する「自己完結型」のシステムである。

今回は、PowerPoint VBAの制約を逆手に取り、アプリケーションレベルのイベントを特定のプレゼンテーションファイル内で完結させる、極限のアーキテクチャを伝授する。

1. なぜ「自己完結」が困難なのか

PowerPointのイベントハンドラは、標準モジュールやスライドモジュールには書けない。`Application`オブジェクトのイベントを拾うには、`WithEvents`を付与したクラスモジュールが必須となる。

多くのエンジニアはここで「アドインが必要だ」と誤解する。だが、`Auto_Open`(あるいは `Workbook_Open` 的なトリガー)の概念が希薄なPowerPointにおいて、「起動時にインスタンスをメモリに常駐させる」ことさえできれば、アドインなど不要なのだ。

2. アーキテクチャの核心:クラスの生存期間管理

ポイントは、`Application`のイベントを監視するクラス(仮に `AppEventHandler` とする)のインスタンスを、どこで保持するかだ。`Standard`モジュール内のグローバル変数として定義し、`Auto_Open`に相当するトリガーから呼び出す。

ステップ1:クラスモジュール `AppEventHandler` の定義

まずは監視の中枢となるクラスを作成する。

‘ クラスモジュール: AppEventHandler
Option Explicit

‘ アプリケーションレベルのイベントを定義
Public WithEvents App As Application

Private Sub App_PresentationBeforeSave(ByVal Pres As Presentation, Cancel As Boolean)
‘ 保存時にログを吐く、あるいは外部APIを叩く等のロジックをここに
Debug.Print “Save event detected: ” & Pres.Name
End Sub

Private Sub App_SlideShowNextSlide(ByVal Wn As SlideShowWindow)
‘ スライド切り替えを検知してシステム連携を行う
Debug.Print “Transition to: ” & Wn.View.Slide.SlideIndex
End Sub

ステップ2:標準モジュールでの常駐化

次に、このクラスをインスタンス化してメモリに固定する。ここで重要なのは「オブジェクトの明示的解放」を考慮した管理だ。

‘ 標準モジュール: MainModule
Option Explicit

‘ インスタンスを保持するグローバル変数
Public EventHandler As AppEventHandler

‘ プレゼンテーションが開かれた瞬間に自動実行されるフック
Sub Auto_Open()
If EventHandler Is Nothing Then
Set EventHandler = New AppEventHandler
Set EventHandler.App = Application
End If
End Sub

【重要】なぜこれで成功するのか
`.pptm`を開いた直後、PowerPointはVBAプロジェクトをロードする。ここで`Auto_Open`を呼ぶ(※)ことで、`EventHandler`変数がヒープ領域に確保され、PowerPointプロセスが生きている限り、監視インスタンスがガベージコレクション(GC)から保護される。

※注:Auto_Openが走らない環境下では、`Presentation_Open`イベントが定義された `ThisPresentation` モジュールを併用する。

3. メモリリークを許さない:ライフサイクル管理の極致

VBAのGCは信用するな。特に`Application`オブジェクトを監視する場合、インスタンスの不適切な保持は、PowerPointを閉じた後もプロセスがゾンビ化する原因となる。

‘ プレゼンテーション終了時のクリーンアップ
Private Sub Presentation_Close()
‘ 明示的な解放
If Not EventHandler Is Nothing Then
Set EventHandler.App = Nothing
Set EventHandler = Nothing
End If
End Sub

4. シニアエンジニアへの提言:API連携への拡張

この実装の真価は、ここから「システム間連携」へと昇華させることにある。例えば、`SlideShowNextSlide` イベント内で `WinHTTP` を利用し、現在のスライド番号を外部の管理用REST APIに非同期で送信する実装だ。

‘ 外部API連携のヒント (WinHTTPを使用)
Private Sub NotifyExternalSystem(slideIndex As Long)
Dim http As Object
Set http = CreateObject(“WinHttp.WinHttpRequest.5.1”)

‘ プロキシ設定や認証ヘッダーを付与して送信
http.Open “POST”, “https://api.your-company.com/log”, False
http.SetRequestHeader “Content-Type”, “application/json”
http.Send “{“”slide””:” & slideIndex & “}”

Set http = Nothing
End Sub

5. 最後に:保守の哲学

このアーキテクチャは「魔法」ではない。`Application`オブジェクトを直接操作するため、他のアドインとイベントの競合を起こす可能性がある。また、VBAの「プロジェクトの保護」を過信せず、コードの難読化や署名プロセスの自動化をCI/CDパイプラインに組み込むことが、プロフェッショナルな現場における正解だ。

アドインという「外部的依存」を削ぎ落とし、ファイルひとつで完結する高潔な設計こそが、我々アーキテクトが目指すべき地平である。


筆者より:
コードをコピペして満足するな。なぜ`WithEvents`がこのメモリ配置で機能し続けるのか、参照カウンタの挙動まで想像を巡らせるのだ。それが、VBAを「ただのスクリプト」から「システム」へと引き上げる唯一の手段である。

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