【テクニカル・上級編】【中級者】OneDriveの「バージョン履歴」を意識し、保存時に自動でローカルの「世代管理フォルダ」にもミラーリング保存する二重バックアップマクロ – PowerPoint VBA解析バイブル

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

クラウドの「全能感」という脆弱性:OneDrive環境におけるPowerPointの原子的なシャドウ・ミラーリング実装

クラウドストレージ、特にOneDriveやSharePointによる「自動保存」と「バージョン履歴」は、一見すると完璧なバックアップ戦略に見える。しかし、我々のようなアーキテクトは、その背後に潜む「同期の遅延」「破損の伝播」「復元プロセスの非決定性」というリスクを看過してはならない。

もし、マクロのバグや、破損したアドインの介入によってプレゼンテーションが破壊された場合、クラウド上の「最新バージョン」は即座に「破損したバージョン」へと上書きされる。バージョン履歴から過去の状態をロールバックする作業は、エンドユーザーにとっては極めて煩雑であり、業務継続性を損なう。

本稿では、クラウドの同期エンジンに依存せず、保存の瞬間に「物理的な世代管理」をローカル環境へ強制的に実行する、堅牢なシャドウ・ミラーリング・アーキテクチャの実装手法を解説する。

1. 設計思想:なぜ `SaveAs` ではなく `SaveCopyAs` なのか

設計における最大の陥穽は、ファイル操作のタイミングである。
通常の `SaveAs` メソッドは、現在開いているオブジェクトの参照先(パス)を書き換えてしまう。これは、ユーザーが作業しているコンテキストを破壊する行為であり、エンジニアリングとして極めて不適切だ。

我々が採用すべきは、`SaveCopyAs` である。
このメソッドは、現在のメモリ上のプレゼンテーション・オブジェクトの状態を、既存のファイルハンドルを維持したまま、別名でディスクに書き出す。これにより、ユーザーの作業フローを阻害することなく、バックアップの原子性(Atomicity)を確保できる。

2. アーキテクチャ構成

本システムは、以下の2つのコンポーネントで構成される。

1. Event Handler Class: PowerPointアプリケーション全体のイベント(`PresentationSave`)を監視する。
2. Core Engine Module: ファイルパスの解析、ディレクトリの動的生成、および物理コピーの実行を担う。

3. 実装:極限の堅牢性を備えたコード

# A. クラスモジュール: `clsAppEvent`

アプリケーションイベントを捕捉するためには、クラスモジュールによるイベントハンドリングが必須である。

' Class Module: clsAppEvent
Option Explicit

' PowerPoint Applicationのイベントを捕捉するための宣言
Private WithEvents App As Application

' 初期化メソッド
Public Sub Initialize(ByVal targetApp As Application)
    Set App = targetApp
End Sub

' プレゼンテーションが保存された瞬間に発火
Private Sub App_PresentationSave(ByVal Pres As Presentation)
    ' コアエンジンを呼び出し、ミラーリングを実行
    ' 実行中の保存処理と競合しないよう、非同期的な振る舞いを意識する
    BackupEngine.ExecuteMirroring Pres
End Sub

# B. 標準モジュール: `BackupEngine`

ここで、Windows API的な思考に基づいた、厳格なエラーハンドリングとオブジェクト管理を行う。

' Standard Module: BackupEngine
Option Explicit

' FileSystemObjectの参照設定は不要とする(Late Bindingによる互換性確保)
' 実行環境のレジストリ依存を最小限に抑えるため

Public Sub ExecuteMirroring(ByVal Pres As Presentation)
    On Error GoTo ErrorHandler
    
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    Dim sourcePath As String
    Dim targetFolder As String
    Dim timestamp As String
    Dim newFileName As String
    Dim fullTargetPath As String
    
    ' 1. 属性情報の抽出
    sourcePath = Pres.FullName
    
    ' OneDrive等のクラウドパス(URL形式)を検知した場合のガード
    ' ローカルパスでない場合は、物理的なバックアップは不可能であるため即時離脱
    If InStr(1, sourcePath, "http", vbTextCompare) > 0 Then
        ' 管理者へのログ出力等の処理をここに検討
        Exit Sub
    End If
    
    ' 2. 世代管理フォルダの構築
    ' ファイルの親ディレクトリ直下に "_Backups" フォルダを生成する設計
    targetFolder = fso.GetParentFolderName(sourcePath) & "\_Backups\"
    If Not fso.FolderExists(targetFolder) Then
        fso.CreateFolder targetFolder
    End If
    
    ' 3. タイムスタンプの生成 (ISO 8601準拠の形式)
    ' ファイル名に使用不可能な文字を排除するため、厳格なフォーマットを指定
    timestamp = Format(Now, "yyyymmdd_hhmmss")
    
    ' 4. 新規ファイル名の構築
    ' 拡張子を分離し、タイムスタンプを注入
    newFileName = fso.GetBaseName(sourcePath) & "_" & timestamp & "." & fso.GetExtensionName(sourcePath)
    fullTargetPath = targetFolder & newFileName
    
    ' 5. 物理コピーの実行 (SaveCopyAs)
    ' メモリ上の現在の状態を、決定論的にディスクへ書き出す
    Pres.SaveCopyAs fullTargetPath
    
    ' --- メモリ解放の儀式 ---
    ' VBAにおけるオブジェクトのライフサイクル管理
    ' 大規模なプレゼンテーションでは、FSOの解放を明示的に行うことがメモリリーク防止に繋がる
    Set fso = Nothing
    Exit Sub

ErrorHandler:
    ' 致命的なエラー(権限不足、ディスクフル等)を捕捉
    ' ユーザーの業務を止めないよう、エラーメッセージは最小限に留め、
    ' 必要に応じてイベントログ等に記録する設計が望ましい
    Debug.Print "Critical Error in BackupEngine: " & Err.Description
    Set fso = Nothing
End Sub

' システム起動時にイベントハンドラをインスタンス化するエントリーポイント
Public Sub InitializeSystem()
    Static AppEvent As clsAppEvent
    
    ' 既にインスタンスが存在する場合は再生成しない(シングルトン的な振る舞い)
    If AppEvent Is Nothing Then
        Set AppEvent = New clsAppEvent
        AppEvent.Initialize Application
        MsgBox "Shadow Mirroring System: Online", vbInformation, "System Status"
    End If
End Sub

4. 技術的深掘り:プロフェッショナルのための知見

オブジェクトのライフサイクルとメモリの重み

VBAにおいて、`CreateObject(“Scripting.FileSystemObject”)` は便利だが、ループ内や頻繁に呼び出されるイベント内で使用する場合、明示的な `Set fso = Nothing` が不可欠である。特に、数GBに及ぶ巨大なプレゼンテーションを扱う場合、オブジェクトの解放遅延は、PowerPoint自体のメモリ領域を圧迫し、最終的に `Out of Memory` エラーを誘発する。

クラウドパス(URL)の罠

OneDriveの同期クライアントが動作している環境では、`Pres.FullName` が `https://d.docs.live.net/…` のようなURL形式を返す場合がある。この状態では、WindowsのファイルシステムAPIは機能しない。
本コードでは、`InStr(1, sourcePath, “http”)` を用いてこれを検知し、物理バックアップが不可能なコンテキストでは処理をサイレントにスキップする「Fail-safe」設計を採用している。

競合状態(Race Condition)への配慮

`PresentationSave` イベントは、ユーザーが「保存」ボタンを押した直後に発火する。この時、OneDriveの同期エンジンがファイルをロックしている可能性がある。
実運用環境においては、`On Error` 句の中で `Err.Number = 70` (Permission Denied) を検知した場合に、数ミリ秒のウェイト(`Sleep` APIの呼び出し)を挟んでリトライするロジックを組み込むことで、同期プロセスとの衝突を回避できる。

結論

真の堅牢性とは、便利な機能(OneDriveの自動保存)を信じることではなく、それが失敗した際の「物理的な退路」をあらかじめ設計しておくことにある。

この「シャドウ・ミラーリング」の実装は、単なるバックアップマクロではない。それは、不確実なクラウド環境において、エンジニアがユーザーの資産を守り抜くための、極めて論理的な防衛策である。

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