【実務・中級編】【社外秘クレンジング】非表示スライド(”ShowInSlideShow = msoFalse”)のみを自動検出し、特定の透かし(ウォーターマーク)を一括挿入・削除するセキュリティマクロ – PowerPoint VBA解析バイブル

スポンサーリンク

【社外秘クレンジング】非表示スライドを狙い撃て!PowerPoint VBAによるセキュリティ・ウォーターマーク自動制御の極意

開発プロジェクトの現場で、こんな修羅場に直面したことはないだろうか。

「来期の経営戦略発表会、非表示スライドに機密データが残っていたから外してくれ」
「社外秘資料のドラフト版に、全スライド……いや、非表示の予備スライドだけに『CONFIDENTIAL』の透かしを入れ忘れた!」

プレゼンテーションの構造化において、非表示スライド(`ShowInSlideShow = msoFalse`)は諸刃の剣だ。発表時には隠れるものの、ファイルとしてはしっかりと外部に渡ってしまう。ここに機密情報や未確定のドラフトデータが残存し、誤送信による情報漏洩を引き起こすインシデントは、企業の信頼を根底から揺るがす。

今回は、PowerPoint VBAのオブジェクトモデルを極限まで理解し、「非表示スライドのみを正確に検出し、ウォーターマーク(透かし)の一括挿入・クレンジング(削除)をミリ秒単位で完遂する」ためのプロダクションコードを伝授する。

中途半端なネットのサンプルコードをコピペして「なぜか動作が重い」「図形が重複してゴミだらけになる」と悩むフェーズは、今日で終わりにしよう。

1. なぜ一般的なコードは「実務で使い物にならない」のか?

多くのプログラマーが書く初歩的なVBAコードは、決まって次のようなアンチパターンに陥っている。

1. 全スライドを無条件にループする
表示・非表示を問わず全スライドを走査し、その中でいちいち `If Slide.SlideShowTransition.Hidden` などと判定する。スライド数が数百枚ある重厚長大な資料では、無駄な処理が走る。
2. ウォーターマークの重複生成
マクロを2回実行した際に、同じ「CONFIDENTIAL」シェイプが何重にも重なり、スライドの見栄えが完全に破壊される。
3. 削除処理のインデックスズレ(前方からの削除)
シェイプコレクションを前から順に `Delete` しようとして、インデックスの整合性が崩れ、削除漏れや実行時エラーが発生する。

チーフアーキテクトとして、我々は「冪等性(何度実行しても結果が同じになる性質)」「高パフォーマンス」を担保した設計でなければならない。

2. 堅牢なセキュリティ・クレンジングツールの設計思想

今回の自動化ツールで実装すべき要件は以下の3点だ。

  • 対象の限定: `ShowInSlideShow = msoFalse` の非表示スライドのみをターゲットにする。
  • 一意性の担保(識別子の付与): 生成するウォーターマークには、コードから一発で特定・削除できる「タグ(Nameプロパティ)」を付与し、重複生成を防ぐ。
  • 動的切替(モードレス設計): 1つのプロシージャ内で、引数やフラグにより「挿入(Insert)」と「クレンジング(Remove)」を安全に切り替える。

それでは、実務の現場でそのまま動かせるプロダクションコードを公開する。

3. コピペで使えるプロダクションコード

以下のコードをVBAエディタ(`Alt + F11`)の標準モジュールに貼り付けてほしい。

Option Explicit

‘ ==============================================================================
‘ módulo名: ModSecurityWatermark
‘ 概要: 非表示スライドに対するセキュリティウォーターマークの挿入・一括削除
‘ 著者: チーフアーキテクト
‘ ==============================================================================

‘ 定数定義(マジックナンバーの排除)
Private Const WATERMARK_TAG_NAME As String = “SEC_WATERMARK_INTERNAL_USE”
Private Const WATERMARK_TEXT As String = “【社外秘 / DRAFT】”

Public Sub RunWatermarkManager()
Dim wsShell As Object
Dim userChoice As VbMsgBoxResult

Set wsShell = CreateObject(“WScript.Shell”)

‘ 実行モードの選択
userChoice = MsgBox(“非表示スライドのセキュリティ処理を選択してください。” & vbCrLf & _
“[はい] : 透かし(ウォーターマーク)を挿入” & vbCrLf & _
“[いいえ] : 透かしを完全にクレンジング(削除)”, _
vbYesNoCancel + vbQuestion, “セキュリティ・ウォーターマーク・マネージャー”)

If userChoice = vbCancel Then Exit Sub

On Error GoTo ErrorHandler

‘ 画面描画を停止し、処理速度を限界まで引き上げる
Application.ScreenUpdating = msoFalse

If userChoice = vbYes Then
Call InsertWatermarkToHiddenSlides(ActivePresentation)
MsgBox “非表示スライドへのウォーターマーク挿入が完了しました。”, vbInformation, “完了”
Else
Call RemoveWatermarkFromPresentation(ActivePresentation)
MsgBox “非表示スライドからのクレンジング(削除)が完了しました。”, vbInformation, “完了”
End If

CleanUp:
Application.ScreenUpdating = msoTrue
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
Resume CleanUp
End Sub

‘ ——————————————————————————
‘ 非表示スライドへのウォーターマーク挿入処理
‘ ——————————————————————————
Private Sub InsertWatermarkToHiddenSlides(ByRef targetPres As Presentation)
Dim sld As Slide
Dim shp As Shape
Dim targetShape As Shape
Dim isAlreadyExists As Boolean

For Each sld In targetPres.Slides
‘ 非表示スライド(ShowInSlideShow = msoFalse)のみを対象とする
If sld.SlideShowTransition.Hidden = msoTrue Then

‘ 既に同じウォーターマークが存在するかチェック(重複生成の防止)
isAlreadyExists = False
For Each shp in sld.Shapes
If shp.Name = WATERMARK_TAG_NAME Then
isAlreadyExists = True
Exit For
End If
Next shp

‘ 存在しない場合のみ新規作成
If Not isAlreadyExists Then
‘ ワードアートまたはテキストボックスとして中央に配置
‘ 座標やサイズはスライドマスターに依存せず、絶対配置する
Set targetShape = sld.Shapes.AddTextEffect( _
PresetTextEffect:=msoTextEffect1, _
Text:=WATERMARK_TEXT, _
FontName:=”Meiryo”, _
FontSize:=54, _
FontBold:=msoTrue, _
FontItalic:=msoFalse, _
Left:=100, Top:=200)

‘ 一意の識別子(Tag)を付与し、後からプログラムで追跡できるようにする
targetShape.Name = WATERMARK_TAG_NAME

‘ セキュリティ視認性を高めるためのスタイリング(半透明・赤系・傾き)
With targetShape
.Rotation = -30 ‘ 斜めに配置
.Fill.ForeColor.RGB = RGB(255, 0, 0) ‘ 赤色
.Transparency = 0.5 ‘ 半透明(50%)

‘ スライドの中央に正確に配置
.Left = (targetPres.PageSetup.SlideWidth – .Width) / 2
.Top = (targetPres.PageSetup.SlideHeight – .Height) / 2

‘ 念のため最背面ではなく最前面に配置して視認性を担保する場合もあるが、
‘ 編集の邪魔になる場合は SendToBack に変更可能
.ZOrder msoBringToFront
End With
End If

End If
Next sld
End Sub

‘ ——————————————————————————
‘ ウォーターマークのクレンジング(全スライド走査による確実な削除)
‘ ——————————————————————————
Private Sub RemoveWatermarkFromPresentation(ByRef targetPres As Presentation)
Dim sld As Slide
Dim i As Long

For Each sld In targetPres.Slides
‘ シェイプコレクションを後ろからループ(インデックスズレによる削除漏れを防ぐ鉄則)
For i = sld.Shapes.Count To 1 Step -1
If sld.Shapes(i).Name = WATERMARK_TAG_NAME Then
sld.Shapes(i).Delete
End If
Next i
Next sld
End Sub

4. チーフアーキテクトが解説する、コードの急所と技術的ポイント

このコードが「実務に耐えうる堅牢性」を持つ理由を3つの視点から解説する。

① `Application.ScreenUpdating = msoFalse` による爆速化

PowerPoint VBAで最もボトルネックになるのは、シェイプの生成やプロパティ変更に伴う「画面の再描画(レンダリング)」だ。数枚なら体感できないが、50枚、100枚とスライドが増えた途端に画面がパカパカと点滅し、処理に数十秒を要するようになる。
処理の冒頭で描画を切り、最後に復帰させることで、処理速度を最大数倍〜十数倍に跳ね上げることができる。

② シェイプの逆順ループ(`Step -1`)の鉄則

削除処理(`RemoveWatermarkFromPresentation`)において、`For i = 1 To sld.Shapes.Count` と書いてはならない。
コレクション内の要素を前から削除すると、インデックスが自動的に繰り上がり、削除処理がスキップされる要素が発生する(バグの温床)。「コレクション要素の削除は常に後ろから(`Step -1`)」は、VBAプログラミングにおける不変の真理である。

③ 一意のタグ(`Name = WATERMARK_TAG_NAME`)による冪等性の担保

何が悲しいかといえば、マクロをうっかり2回実行したせいで、スライド上に「【社外秘】」の文字が何重にも重なり、読めなくなることだ。
本コードでは、生成したシェイプの `.Name` プロパティに独自の識別子を刻み込む。これにより、2回目に実行した際は「すでに存在する」と判定され、重複生成が完全にブロックされる。

5. 実運用における拡張アイデア:外部データベースやクラウド連携へ

このコードをベースにすれば、さらに高度なセキュリティパイプラインを構築できる。

  • Git / CI/CDパイプラインとの統合:

社内ニッチなサーバーで動くVBScript(外部からPowerPointを操作するスクリプト)にラップし、社内Gitにプレゼン資料がコミットされたタイミングで、非表示スライドに自動でウォーターマークを焼き込む。

  • 分類ラベル(Information Protection)連携:

Microsoft Purviewの秘密度ラベルと連携し、ラベルが「秘密(Confidential)」に設定されている場合のみ、このマクロを自動トリガーしてクレンジングを行う。

総括

PowerPoint VBAは、単なる「めんどくさい作業を肩代わりするおもちゃ」ではない。企業のセキュリティポリシーを担保し、ヒューマンエラーによる情報漏洩事故を物理的にシャットアウトする「攻めの防衛システム」として機能させることができる。

オブジェクトモデルのライフサイクルを正しく理解し、堅牢なアーキテクチャで組まれたコードは、あなたの開発者としてのバリューを圧倒的に高めてくれるはずだ。

現場の信頼を勝ち取るスマートな自動化を、今日から君の手で実装してほしい。

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