情報の放出を制御せよ: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.
