【実務・中級編】【初心者】「読み取り専用推奨」属性を設定して別名保存し、他ユーザーによる不用意な編集を防止するセキュリティマクロ – PowerPoint VBA解析バイブル

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

「マスターを守れ」——配布用PowerPointを「読み取り専用推奨」で制御する、堅牢な自動化設計の極意

現場の自動化エンジニアとして活動していると、避けては通れない「悲劇」がある。

それは、「配布したはずのテンプレートやマスター資料が、誰かの不用意な上書きによって破壊され、元に戻せなくなる」という事態だ。

「読み取り専用属性(ファイルシステムレベル)」を付与すればいいのではないか? と考える初学者は多い。しかし、それは設計として不完全だ。OSレベルの属性変更は権限の問題に抵触しやすく、またユーザーが「書き込み権限」を持っている限り、容易に解除されてしまう。

我々プロフェッショナルが目指すべきは、ファイルシステムに頼るのではなく、PowerPointのプレゼンテーション・オブジェクトそのものに「読み取り専用を推奨する」という意思(プロパティ)を埋め込み、ユーザーの行動を心理的に制御する設計である。

本記事では、単なるコードの書き方ではなく、業務ツールとして「バグを許さない」「運用に耐えうる」堅牢な保存ロジックの設計思想を伝授する。

1. 概念設計:なぜ「属性」ではなく「プロパティ」なのか

まず、設計思想を整理しよう。セキュリティレベルには二つの階層がある。

1. ファイルシステム・レベル(OS制御): ファイル自体の属性を `ReadOnly` にする。これは「書き込み権限がない」状態を作るが、ネットワークドライブや共有フォルダの権限設定に依存するため、環境によって挙動が不安定になりやすい。
2. アプリケーション・レベル(Office制御): PowerPointの `ReadOnlyRecommended` プロパティを `True` にする。これはファイルを開く際に「読み取り専用で開きますか?」というダイアログを強制的に表示させるものだ。

業務自動化ツールとして配布する場合、後者を選択すべきだ。なぜなら、「ユーザーに選択肢を与えつつ、誤操作のコスト(ダイアログを閉じて編集を継続する手間)を上げる」ことで、心理的な防壁を築けるからだ。

2. 堅牢な実装への3つの原則

コードを書く前に、アーキテクトとして以下の3原則を叩き込んでおくこと。

① 外部依存の分離(FileSystemObjectの活用)

標準の `Dir` 関数だけでファイル操作を行うのは、プロの仕事ではない。ファイルが存在するか、フォルダが作成可能かといった判定には、`Scripting.FileSystemObject` を使い、オブジェクト指向的に堅牢な判定を行うべきだ。

② エラーハンドリングの徹底

「保存先フォルダが存在しない」「ファイルが既に開かれている」「書き込み権限がない」……。保存処理には、常に例外(Exception)が付きまとう。これらを無視するコードは、現場では「動かないゴミ」と同義だ。

③ 副作用の管理

「別名保存」を行う際、元のファイル(マスター)の状態を汚染してはならない。必ず「新しいインスタンス」として、あるいは「コピー」として保存プロセスを完結させる必要がある。

3. プロダクションコード:SecureSaveModule

以下に、そのまま業務に投入できるレベルのコードを示す。このコードは、単に保存するだけでなく、ディレクトリの存在確認、重複チェック、そして `ReadOnlyRecommended` の設定をシームレスに行う。

' ==============================================================================
' Module: SecureSaveModule
' Description: 配布用ファイルの安全な別名保存(読み取り専用推奨属性付き)
' Author: Chief Architect
' ==============================================================================

Option Explicit

''' <summary>
''' プレゼンテーションを「読み取り専用推奨」属性を付与して別名保存する。
''' </summary>
''' <param name="targetPath">保存先のフルパス(例: "C:\Output\Report_2023.pptx")</param>
Public Sub SaveAsReadOnlyRecommended(ByVal targetPath As String)
    Dim fso As Object
    Dim pptPres As Presentation
    Dim parentFolder As String
    
    ' 1. オブジェクトの初期化
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set pptPres = ActivePresentation
    
    ' 2. パス解析
    On Error Resume Next
    parentFolder = fso.GetParentFolderName(targetPath)
    On Error GoTo 0
    
    If parentFolder = "" Then
        MsgBox "エラー: 無効なパスが指定されました。", vbCritical, "設計ミス"
        Exit Sub
    End If

    ' 3. エラーハンドリング・ブロックの開始
    On Error GoTo ErrorHandler

    ' 4. 保存先ディレクトリの存在確認
    If Not fso.FolderExists(parentFolder) Then
        ' フォルダがない場合は、自動作成するかエラーにするか。
        ' ここでは堅牢性を重視し、作成せずエラーとする設計をとる。
        Err.Raise vbObjectError + 513, "SaveAsReadOnlyRecommended", "保存先フォルダが存在しません: " & parentFolder
    End If

    ' 5. 同名ファイルの存在チェック
    If fso.FileExists(targetPath) Then
        Dim res As VbMsgBoxResult
        res = MsgBox("既に同名のファイルが存在します。上書きしますか?" & vbCrLf & targetPath, _
                     vbQuestion + vbYesNo, "上書き確認")
        If res = vbNo Then
            MsgBox "保存をキャンセルしました。", vbInformation
            Exit Sub
        End If
    End If

    ' 6. 【核心】プロパティの設定と保存実行
    ' 読み取り専用推奨フラグをTrueに設定
    pptPres.ReadOnlyRecommended = True
    
    ' 保存実行(ppSaveAsDefaultを使用)
    ' 注意: SaveAsを使用することで、現在のセッションのファイルは新しいファイルへ切り替わる
    pptPres.SaveAs Filename:=targetPath, FileFormat:=ppSaveAsDefault
    
    MsgBox "配布用ファイルを安全に保存しました。" & vbCrLf & targetPath, vbInformation, "完了"

CleanUp:
    Set fso = Nothing
    Set pptPres = Nothing
    Exit Sub

ErrorHandler:
    ' 7. エラーのロギングと通知
    ' 現場ではここでログファイルやDBへエラーを書き出すのが定石
    MsgBox "致命的なエラーが発生しました。" & vbCrLf & _
           "エラー番号: " & Err.Number & vbCrLf & _
           "内容: " & Err.Description, vbCritical, "実行時エラー"
    Resume CleanUp
End Sub

''' <summary>
''' テスト用メインルーチン
''' </summary>
Public Sub Test_SecureSave()
    ' 実際の運用では、UI(ダイアログ)から取得したパスを渡す
    Dim testPath As String
    testPath = ThisPresentation.Path & "\Distribute_Template.pptx"
    
    Call SaveAsReadOnlyRecommended(testPath)
End Sub

4. アーキテクトの視点:運用上の注意点

このコードを導入する際、以下の3点についてステークホルダー(利用者)と合意形成を図っておく必要がある。

データベース連携における注意

もし、この保存プロセスをデータベース(SQL ServerやAccess等)の管理下で行う場合、`targetPath` をDBに記録する際は「相対パス」ではなく必ず「絶対パス」を使用せよ。また、保存完了後にDBのステータスを「配布済み」に更新するトランザクション処理を忘れてはならない。

セキュリティの限界を知る

繰り返すが、`ReadOnlyRecommended` は「親切なリマインダー」であり、「鉄壁のロック」ではない。悪意のあるユーザーや、極めて不慣れなユーザーは、ダイアログを無視して編集を強行できる。
真の機密情報を扱う場合は、VBAによる制御ではなく、Microsoft Purview (Information Protection) 等の、ファイルそのものに暗号化と権限を紐付けるソリューションを検討すべきだ。

パフォーマンスの重み

大量のファイルを一括変換(PDF化など)するループ処理の中で、この `SaveAs` を呼び出す場合、ファイルI/Oのオーバーヘッドは無視できない。一括処理を行う際は、`ScreenUpdating = False`(PowerPointでは直接的なプロパティはないが、表示を最小限にする工夫)や、適切なメモリ解放を意識した設計を行え。

結論

「動けばいい」というコードを書くのは、作業員だ。
「想定外の事態をあらかじめ設計に組み込む」コードを書くのが、エンジニアだ。

今回解説した `ReadOnlyRecommended` の制御は、小さな一歩かもしれない。しかし、こうした「ユーザーのミスを未然に防ぐ設計」の積み重ねこそが、システム全体の信頼性を形作るのである。

さあ、君の書くコードに、プロフェッショナルの魂を込めろ。

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