【入門編】【上級者】PowerPointの保存前イベント(PresentationBeforeSave)をフックし、特定のセキュリティ基準を満たさない場合に保存を強制キャンセルするセキュリティアドイン – PowerPoint VBA解析バイブル

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

マクロ記録の先へ。PowerPointの「意志」を制御する——セキュリティアドイン開発への招待

「マクロの記録」ボタンを押して、生成されたコードを眺めて「ふーん、こんな感じか」と納得する。
もしあなたが今、そんな段階にいるとしたら……おめでとうございます。あなたは自動化の第一歩を、正しく踏み出しました。

しかし、プロフェッショナルの世界、つまり「業務自動化エンジニア」の世界では、マクロの記録は単なる「辞書」に過ぎません。私たちが目指すのは、ユーザーの操作を待つだけのプログラムではなく、アプリケーションの挙動そのものを監視し、制御する「知能」です。

今日、皆さんに伝授するのは、PowerPoint VBAの真髄の一つである「イベントハンドリング」。
具体的には、ユーザーが「保存」を押した瞬間に割り込み、機密情報が含まれていればその保存を強制的に阻止する――そんな、企業のセキュリティを守る「守護神(アドイン)」の作り方です。

ここをマスターすれば、あなたは「操作を代行する人」から「仕組みを設計する人」へと進化できるはずです。準備はいいですか?

1. 「イベント」を掌握する:プログラムに「耳」を持たせる

通常のVBAは、あなたが「実行」ボタンを押したときにだけ動きます。これを「命令型」と呼びます。
しかし、今回私たちが作りたいのは、ユーザーが「保存」というアクションを起こした瞬間に、自動的に反応する仕組みです。これを「イベント駆動型(Event-Driven)」と呼びます。

PowerPointという巨大なアプリケーションには、無数の「イベント」が潜んでいます。

  • スライドが切り替わったとき (`SlideShowNextSlide`)
  • 新しいプレゼンテーションが開いたとき (`PresentationOpen`)
  • そして今回、保存しようとしたとき (`PresentationBeforeSave`)

これらを検知するためには、プログラムの中に「アプリケーションの動きを常に聞き耳を立てて監視する専用の窓口」を作る必要があります。これが、後ほど解説する「クラスモジュール」の役割です。

2. 実装の設計図:監視役(Class)と司令塔(Module)

この仕組みを作るには、2つの部品が必要です。

1. クラスモジュール(監視役): `WithEvents` という特殊な宣言を使い、PowerPointアプリケーションの動きを常時監視する「耳」の役割を果たします。
2. 標準モジュール(司令塔): 監視役を起動し、アプリが動いている間、監視役が消えないように保持する役割を果たします。

ここで、エンジニアとしての重要な知見を一つ。
「オブジェクトのライフサイクル」を意識してください。
VBAの変数(インスタンス)は、プロシージャ(関数)が終わると消滅してしまいます。監視役をプロシージャの中で作ってしまうと、作った瞬間に監視が終了してしまいます。だからこそ、監視役は「グローバル変数」として、アプリが動いている間ずっと生存させ続けなければならないのです。

3. 実践:セキュリティアドインのコード

それでは、実際にコードを書いていきましょう。
今回は、「スライド内のテキストに『機密』という言葉が含まれている場合、保存をキャンセルする」というルールを設けます。

ステップ1:クラスモジュールの作成

VBAエディタで「挿入」→「クラスモジュール」を選択し、プロパティウィンドウで名前を `clsAppEvent` に変更してください。

' クラス名: clsAppEvent
Option Explicit

' PowerPointアプリケーションのイベントをキャッチするための変数
' WithEvents を使うことで、Applicationオブジェクトのイベントにアクセス可能になる
Private WithEvents App As Application

' 監視を開始するための初期化メソッド
Public Sub Init(ByVal targetApp As Application)
    Set App = targetApp
End Sub

' 【重要】プレゼンテーションが保存される直前に発生するイベント
' Cancel 引数を True にすることで、保存処理を強制的に中断できる
Private Sub App_PresentationBeforeSave(ByVal Pres As Presentation, Cancel As Boolean)
    Dim sld As Slide
    Dim shp As Shape
    Dim isViolationFound As Boolean
    Dim sensitiveWord As String
    
    ' 検知したい機密キーワード
    sensitiveWord = "機密"
    isViolationFound = False
    
    ' 1. 全てのスライドをスキャンする
    For Each sld In Pres.Slides
        ' 2. 各スライド内の全図形をスキャンする
        For Each shp In sld.Shapes
            ' テキストを持っている図形かどうかを判定
            If shp.HasTextFrame Then
                If shp.TextFrame.HasText Then
                    ' 3. テキスト内に禁止ワードが含まれているかチェック
                    If InStr(shp.TextFrame.TextRange.Text, sensitiveWord) > 0 Then
                        isViolationFound = True
                        Exit For
                    End If
                End If
            End If
        Next shp
        If isViolationFound Then Exit For
    Next sld

    ' 4. 違反が見つかった場合の処理
    If isViolationFound Then
        ' ユーザーに警告を表示
        MsgBox "【警告】機密情報(キーワード: " & sensitiveWord & ")が含まれています。" & vbCrLf & _
               "この状態での保存はセキュリティ規定により禁止されています。", _
               vbCritical, "セキュリティ・ポリシー違反"
        
        ' ここが魔法の一行。CancelをTrueにすることで、保存動作をキャンセルする
        Cancel = True
    End If
End Sub

ステップ2:標準モジュールの作成

次に、「挿入」→「標準モジュール」を選択し、以下のコードを記述します。

' 標準モジュール
Option Explicit

' 監視役のインスタンスを保持するためのグローバル変数
' これを標準モジュールの先頭(プロシージャの外)で宣言することで、
' マクロが終了しても監視役が消えずに残り続ける(ライフサイクルの維持)
Private AppWatcher As clsAppEvent

' アドイン(監視)を開始するメインルーチン
Sub StartSecurityMonitor()
    ' 監視役のインスタンスを作成
    Set AppWatcher = New clsAppEvent
    
    ' 現在のアプリケーションを監視役へ渡して起動
    AppWatcher.Init Application
    
    MsgBox "セキュリティ監視を開始しました。" & vbCrLf & _
           "機密ワードが含まれる場合の保存をブロックします。", vbInformation
End Sub

' 監視を停止するルーチン
Sub StopSecurityMonitor()
    ' インスタンスを解放することで監視を終了させる
    Set AppWatcher = Nothing
    MsgBox "セキュリティ監視を停止しました。", vbExclamation
End Sub

4. プロの視点:陥りやすい罠と回避術

このコードを動かしてみて、「うまく動かないな?」と感じる場面があるかもしれません。プロの現場でよく遭遇するトラブルと、その解決策を伝授します。

① 「監視がすぐに止まってしまう!」問題

これは、先ほど述べた「オブジェクトの寿命(スコープ)」の問題です。
もし `Set AppWatcher = New clsAppEvent` を `Sub StartSecurityMonitor()` の中だけで宣言していたら、関数が終わった瞬間に `AppWatcher` はメモリから消え、監視は終了します。必ずモジュールの最上部(宣言部)で `Private` 変数として定義してください。

② パフォーマンスへの影響

今回のコードは、保存のたびに「全スライドの全図形」をループで回しています。スライドが1,000枚あるプレゼンテーションでこれをやると、保存ボタンを押してから数秒間、PowerPointがフリーズしたようになります。
実務レベルでは、以下のような工夫を検討します:

  • 対象を絞る: 「特定のタグが付いたスライドだけ」を対象にする。
  • インデックス化: 編集中のときだけチェックする。

③ 無限ループの恐怖

もし、`PresentationBeforeSave` イベントの中で、さらに「ファイルを保存するコード(`Pres.Save`)」を書いてしまったらどうなるでしょうか?
「保存しようとする」→「イベント発生」→「中で保存を実行」→「またイベント発生」……。
これが無限ループです。イベントハンドラ内では、自分自身を再び呼び出すようなアクションは極力避けるのが鉄則です。

まとめ:あなたは「仕組み」を作る側へ

お疲れ様でした。
単なる「操作の記録」から脱却し、アプリケーションのイベントを横取りして制御する、高度なアーキテクチャに触れることができました。

今回学んだ「クラスモジュールによるイベントのキャッチ」と「オブジェクトのライフサイクル管理」は、PowerPointだけでなく、ExcelやWord、あるいはVB.NETなどの開発においても共通する、極めて重要な概念です。

「もし、このプログラムがもっと賢くなったら?」
「もし、保存だけでなく、印刷も制限できたら?」

そう考え始めたとき、あなたのエンジニアとしての旅は、本当の意味で始まりました。
次は、もっと複雑な条件、もっと巨大なシステムに挑んでみてください。応援していますよ。

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