【テクニカル・上級編】【中級者】スライド内に散在するすべてのテキストフレームから特定部門の機密キーワードを正規表現で自動検知し、該当箇所の文字列を伏字に置換してから別名保存するプライバシー保護マクロ – PowerPoint VBA解析バイブル

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

情報の放出を制御せよ:PowerPointにおける正規表現を用いた機密情報マスキングの極致

資料の送付は、情報の「放出」である。

外部パートナーやクライアントへプレゼンテーション資料を渡す際、我々が最も恐れるべきは、意図せぬプロジェクトコード、個人名、あるいは未公開の数値の流出だ。多くのエンジニアは、これを「目視」という極めて不確実で、ヒューマンエラーの温床となるプロセスに委ねている。あるいは、単純な `Replace` 関数による文字列置換で済ませようとする。

だが、それはプロの仕事ではない。

文字列置換は、パターンを持たない固定値には機能するが、複雑なプロジェクトコード(例:`PRJ-2023-XXXX`)や、特定の命名規則を持つ機密情報には無力だ。我々に必要なのは、正規表現(Regular Expression)による決定論的な検知と、オブジェクトのライフサイクルを完全に制御した、堅牢な自動化アーキテクチャである。

本稿では、スライド内の全テキストフレームを再帰的に走査し、機密情報を検知・伏字化(マスキング)した上で、元のファイルを破壊せずに別名保存する、実戦投入レベルのプライバシー保護マクロについて解説する。

1. 抽象化されたオブジェクト階層への挑戦

PowerPointのオブジェクトモデルは、Excelに比べると階層構造の「深さ」と「複雑さ」が厄介だ。単に `ActivePresentation.Slides` をループすれば済む話ではない。

スライド内には、単一の `Shape` だけでなく、GroupShape(グループ化された図形)、SmartArt、さらには表(Table)が存在する。これらを無視したスキャンは、機密情報の「死角」を生む。真のアーキテクトは、図形がグループ化されている場合、その内部へ再帰的に潜り込み(Recursive Traversal)、すべての `TextFrame` を漏らさず捕捉するロジックを構築しなければならない。

2. メモリ管理とCOMオブジェクトの生存期間

VBAにおけるパフォーマンスのボトルネックは、多くの場合、CPU演算ではなくCOM(Component Object Model)インターフェースの呼び出しオーバーヘッドにある。

特に `VBScript.RegExp` を使用する場合、正規表現エンジンは外部のCOMオブジェクトとして動作する。ループ内で毎回エンジンをインスタンス化するような愚行は、メモリの断片化と実行速度の劇的な低下を招く。エンジンは一度だけ生成し、スキャンが完了した瞬間に明示的に `Set = Nothing` で解放する。これが、大規模なスライド群を処理する際の鉄則だ。

3. 実装:機密情報マスキング・エンジン

以下に、シニアエンジニアが実装すべき、堅牢なマクロのコードを示す。このコードは、再帰的スキャン、正規表現によるパターンマッチング、およびメモリ最適化を組み込んでいる。

Option Explicit

' ==============================================================================
' Module: PrivacyProtector
' Description: 機密情報を正規表現で検知し、マスキング処理を行う高度な保護エンジン
' Author: Legendary Chief Architect
' ==============================================================================

''' <summary>
''' メインエントリーポイント。プレゼンテーション全体をスキャンし、別名保存する。
''' </summary>
Public Sub ExecutePrivacyMasking()
    Dim targetPres As Presentation
    Dim savePath As String
    Dim regExp As Object
    Dim pattern As String
    
    On Error GoTo ErrorHandler
    
    ' 1. 環境セットアップ
    Set targetPres = ActivePresentation
    
    ' 検知したい機密情報の正規表現パターン(例:プロジェクトコードとメールアドレス)
    ' 実際の実装では、外部設定ファイルやDBから読み込む設計が望ましい
    pattern = "(PRJ-\d{4}-\d{4})|([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})"
    
    ' 2. 正規表現エンジンの初期化(COMオブジェクトの明示的生成)
    Set regExp = CreateObject("VBScript.RegExp")
    With regExp
        .Global = True
        .IgnoreCase = True
        .Pattern = pattern
    End With
    
    ' 3. 保存パスの決定(元のファイル名に _masked を付与)
    savePath = Replace(targetPres.FullName, ".pptx", "_masked.pptx")
    If savePath = targetPres.FullName Then
        MsgBox "ファイルを保存してから実行してください。", vbCritical
        Exit Sub
    End If

    ' 画面更新の停止(PowerPointにはExcelのようなScreenUpdatingはないが、
    ' 処理中のUI干渉を最小限にするため、慎重な設計が必要)
    Debug.Print "--- Masking Process Started ---"

    ' 4. スライド走査の開始
    Dim sld As Slide
    For Each sld In targetPres.Slides
        ProcessShapes sld.Shapes, regExp
    Next sld

    ' 5. 別名保存
    targetPres.SaveCopyAs savePath
    Msg    Print "--- Masking Process Completed Successfully ---"
    MsgBox "処理が完了しました。保存先: " & savePath, vbInformation

CleanUp:
    ' 6. メモリ解放(極めて重要:COMオブジェクトのライフサイクルを管理)
    Set regExp = Nothing
    Exit Sub

ErrorHandler:
    MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
    Resume CleanUp
End Sub

''' <summary>
''' 図形コレクションを再帰的に走査する。グループ化された図形にも対応。
''' </summary>
Private Sub ProcessShapes(ByRef shpRange As Shapes, ByRef regExp As Object)
    Dim shp As Shape
    Dim i As Integer
    
    For Each shp In shpRange
        ' A. グループ化された図形の場合、再帰的に内部を探索
        If shp.Type = msoGroup Then
            ProcessShapes shp.GroupItems, regExp
            
        ' B. テキストフレームを持つ図形の場合
        ElseIf shp.HasTextFrame Then
            If shp.TextFrame.HasText Then
                MaskText shp.TextFrame.TextRange, regExp
            End If
            
        ' C. テーブル(表)の場合
        ElseIf shp.HasTable Then
            Dim r As Integer, c As Integer
            For r = 1 To shp.Table.Rows.Count
                For c = 1 To shp.Table.Columns.Count
                    MaskText shp.Table.Cell(r, c).Shape.TextFrame.TextRange, regExp
                Next c
            Next r
        End If
    Next shp
End Sub

''' <summary>
''' TextRangeに対して正規表現を用いた置換処理を行う。
''' </summary>
Private Sub MaskText(ByRef tr As TextRange, ByRef regExp As Object)
    ' 正規表現にマッチするかを事前にチェック(無駄な書き込み操作を避ける)
    If regExp.Test(tr.Text) Then
        ' マスキング文字列
        Const MASK_STR As String = "[CONFIDENTIAL]"
        ' 置換実行
        tr.Text = regExp.Replace(tr.Text, MASK_STR)
    End If
End Sub

4. アーキテクチャの深層解説

再帰的探索によるカバレッジの確保

`ProcessShapes` 関数において、`shp.Type = msoGroup` の判定を行い、自分自身を呼び出す(Recursive Call)構造を採用している。これにより、ユーザーが「図形をグループ化」して隠蔽したテキストも、確実に捕捉できる。これは、単なるループ処理では到達できない領域だ。

正規表現エンジンの最適化

`MaskText` 内で `regExp.Test(tr.Text)` を呼び出している点に注目してほしい。これは、マッチしないテキストに対して `Replace` 操作(書き込み処理)を行わせないためのガード句である。PowerPointのオブジェクトモデルにおいて、`TextRange.Text` への書き込みは、その図形の再描画をトリガーするため、非常にコストが高い。この「判定してから書き込む」というステップが、大量のスライドを処理する際のパフォーマンスを左右する。

レガシー環境への配慮と保守性

`CreateObject(“VBScript.RegExp”)` を使用することで、参照設定(Early Binding)によるライブラリの依存関係を回避し、Late Binding を実現している。これにより、異なるバージョンのOfficeが混在する社内環境においても、参照不可エラー(Missing Reference)を回避し、高いポータビリティを確保している。

5. さらなる高みへ:システム間連携への展望

このマクロを単体で動かすのは、まだ第一段階に過ぎない。

真のシステム管理者であれば、このロジックを Windows Task Scheduler から呼び出すバッチファイル、あるいは Python (pywin32) を介したバックエンドプロセスへと昇華させるべきだ。例えば、特定の共有フォルダにPowerPointが投入された瞬間、このエンジンが自動起動し、機密情報が含まれていれば隔離フォルダへ移動させ、クリーンなものだけを承認フローに乗せる、といったパイプラインの構築である。

技術は、単なる「便利ツール」ではない。それは、組織の資産を守るための「防壁」であるべきだ。

Copyright © 2024 Legendary Architect. All rights reserved.

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