PowerPoint VBAの「ネットワーク共有」という地雷を突破する:堅牢なリトライ処理の実装術
現場でPowerPoint VBAを触るエンジニアが真っ先にぶち当たる壁。それが「ネットワーク共有ファイルでの保存エラー」だ。
「誰かが開いている」「同期が間に合っていない」「ネットワークが一瞬切れた」。理由は様々だが、VBAがこれらの外乱に対して脆弱なのは事実だ。コードがクラッシュし、未保存データが消し飛ぶ。そんな悲劇を繰り返さないための、「戦場で生き残るためのアーキテクチャ」を伝授する。
—
1. なぜ「単純なOpen」は危険なのか?
多くの初心者は `Presentations.Open(path)` を疑いなく実行する。しかし、企業ネットワークやクラウドストレージ(SharePoint/OneDrive等)では、バックグラウンドでの同期プロセスや他者とのロック競合により、このメソッドは高確率で例外を吐く。
我々が実装すべきは、「開くまで、保存できるまで、諦めずに叩き続ける」という指数バックオフを意識したリトライロジックだ。
—
2. 堅牢なファイル操作のプロトタイプ
以下は、私がプロジェクトで標準採用している「安全なファイル操作」のテンプレートだ。これをモジュール化し、すべてのファイル操作の入り口に配置してほしい。
Option Explicit
‘ 最大リトライ回数と待機時間(秒)
Private Const MAX_RETRIES As Long = 5
Private Const WAIT_SECONDS As Long = 2
”’
”’
Public Function SafeOpenPresentation(ByVal filePath As String) As Presentation
Dim retryCount As Long
Dim prs As Presentation
On Error Resume Next
Do While retryCount < MAX_RETRIES
Set prs = Presentations.Open(filePath, WithWindow:=msoTrue)
If Err.Number = 0 Then
Set SafeOpenPresentation = prs
Exit Function
End If
' ログ出力(必要に応じて)
Debug.Print "Open failed. Retrying... (" & retryCount + 1 & "/" & MAX_RETRIES & ")"
retryCount = retryCount + 1
Sleep (WAIT_SECONDS 1000) ' Windows APIのSleepを使用
Err.Clear
Loop
On Error GoTo 0
Err.Raise vbObjectError + 1001, "SafeOpen", "ファイルを開くことに失敗しました: " & filePath
End Function
'''
”’
Public Sub SafeSavePresentation(ByRef prs As Presentation)
Dim retryCount As Long
On Error Resume Next
Do While retryCount < MAX_RETRIES prs.Save If Err.Number = 0 Then Exit Sub Debug.Print "Save failed. Retrying... (" & retryCount + 1 & "/" & MAX_RETRIES & ")" retryCount = retryCount + 1 Sleep (WAIT_SECONDS 1000) Err.Clear Loop On Error GoTo 0 Err.Raise vbObjectError + 1002, "SafeSave", "ファイルの保存に失敗しました。" End Sub ' Windows API: Sleep関数 If VBA7 Then Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Else Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) End If ---
3. 実装の極意:なぜこの設計なのか
① `On Error Resume Next` の限定的利用
このコードではリトライループの中だけで `On Error` を制御している。決してモジュール全体をエラー無視で包んではいけない。 それは「バグを隠蔽する」行為であり、後々のデバッグを不可能にするからだ。
② 指数バックオフの考え方
上記のコードは固定待機だが、より高度な環境では `WAIT_SECONDS retryCount` のように待機時間を延ばすことを推奨する。ネットワークの混雑時に短時間で叩き続けると、逆にサーバーから「攻撃」とみなされる可能性があるからだ。
③ 「ウィンドウなし」の危険性
`Presentations.Open` の引数 `WithWindow` を `msoFalse` にしたくなる衝動は抑えること。バックグラウンドで開くと、予期せぬダイアログ(読み取り専用の確認など)で処理がハングアップした際、プロセスがゾンビ化して残る。GUIを伴う操作の方が、デバッグや強制終了の観点からは圧倒的に安全だ。
—
4. 開発者への忠告:クラウドストレージの罠
SharePointやOneDrive上で運用する場合、ファイルパスはローカルの同期フォルダ(`C:\Users\…`)を指すことが多い。この場合、ファイルそのもののロックよりも「同期エンジンとの競合」が最大の敵となる。
- 鉄則1: 自動保存機能がオンの場合、VBA側で `Save` を投げると同期と衝突する。運用ルールとして「同期を一時停止するコマンド」をVBAに組み込むか、あるいはファイル名を都度変更して別名保存する運用を検討せよ。
- 鉄則2: ファイルのフルパスが260文字を超えていないか確認せよ。Windows APIの制限により、深い階層にあるファイルは、リトライ処理以前の問題としてアクセスできないケースが多い。
—
結びに代えて
自動化とは、単にコードを書くことではない。「何が起きてもシステムが壊れない状態(フォールトトレランス)」を設計することだ。
今日紹介したコードは、あくまで「最低限の防波堤」に過ぎない。しかし、このリトライロジックをすべてのファイル操作に導入するだけで、君のツールは「動いたり動かなかったりするおもちゃ」から「業務を支える堅牢なインフラ」へと進化する。
次は、このコードを共通モジュール(.bas)として切り出し、チームの資産とせよ。健闘を祈る。
