ネットワーク共有上の「死の競合」を制する:PowerPoint VBAの堅牢なファイルI/Oアーキテクチャ
ネットワーク越しに配置されたPowerPointファイルは、常に「非同期の罠」に晒されている。特にクラウドストレージの同期プロセスや、ファイルサーバーの排他制御が絡む環境では、VBA標準の `Presentations.Open` はあまりに無防備だ。
「ファイルが使用中です」というダイアログでスクリプトを停止させ、サーバー管理者に泣きつく時代は終わらせよう。本稿では、シニアエンジニアが現場で生き残るための、リトライロジックを組み込んだ堅牢なファイルハンドリング手法を伝授する。
—
1. なぜ、標準の Open メソッドでは不十分なのか
VBAの `Presentations.Open` は、ロックされているファイルに対して即座にランタイムエラー(エラー番号:-2147467259)を投げる。このエラーを放置すれば、プロセスはゾンビ化し、メモリリークを誘発する。
我々が実装すべきは、「確率論を考慮した指数バックオフによるリトライ」と「Windows APIを介したロック状態の事前検知」だ。
実装の要諦
1. APIによる排他チェック: `CreateFile` APIを用い、ファイルが「現在読み取り専用で開けるか」を事前にテストする。
2. ランダム・バックオフ: 競合時は単純な待機ではなく、ミリ秒単位でランダムなウェイトを入れ、サーバーのI/O負荷を分散させる。
3. オブジェクトの明示的解放: メモリの断片化を防ぐため、`Presentation` オブジェクトをループ内で厳密に破棄する。
—
2. 実装コード:`RobustFileHandler`
以下は、ファイルオープンと保存における「競合」を力技でねじ伏せるためのモジュールだ。
Option Explicit
‘ ファイルの排他チェック用API
Private Declare PtrSafe Function CreateFile Lib “kernel32” Alias “CreateFileA” ( _
ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, _
ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, _
ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As LongPtr
Private Declare PtrSafe Function CloseHandle Lib “kernel32″ (ByVal hObject As LongPtr) As Long
Private Const GENERIC_READ = &H80000000
Private Const FILE_SHARE_READ = &H1
Private Const OPEN_EXISTING = 3
Private Const INVALID_HANDLE_VALUE = -1
‘ リトライ構成定数
Private Const MAX_RETRIES = 5
Private Const BASE_WAIT_MS = 1000
”’
”’
Public Function SafeOpenPresentation(ByVal filePath As String) As Presentation
Dim i As Integer
Dim pres As Presentation
For i = 1 To MAX_RETRIES
If IsFileAvailable(filePath) Then
On Error Resume Next
Set pres = Presentations.Open(filePath, WithWindow:=msoFalse)
If Err.Number = 0 Then
Set SafeOpenPresentation = pres
Exit Function
End If
On Error GoTo 0
End If
‘ サーバー負荷を考慮したランダムウェイト
SleepInt (BASE_WAIT_MS i) + (Rnd 500)
Next i
Err.Raise 9999, “SafeOpen”, “ファイルオープンに失敗しました。接続を確認してください。”
End Function
‘ ファイルがアクセス可能かAPIで疎通確認
Private Function IsFileAvailable(ByVal filePath As String) As Boolean
Dim hFile As LongPtr
hFile = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0)
If hFile <> INVALID_HANDLE_VALUE Then
CloseHandle hFile
IsFileAvailable = True
Else
IsFileAvailable = False
End If
End Function
‘ OSに制御を戻すための待機処理
Private Sub SleepInt(ms As Long)
Application.Wait Now + TimeSerial(0, 0, ms / 1000)
End Sub
—
3. シニアエンジニアが意識すべき「メモリとライフサイクル」
上記のコードを運用する際、最も重要なのが「オブジェクトのスコープ管理」だ。
- `WithWindow:=msoFalse` の徹底:
バックグラウンド処理において、プレゼンテーションウィンドウを生成してはならない。ウィンドウはGDIリソースを食いつぶす。一括変換処理などでこれを怠れば、数千ファイルの処理中に必ず PowerPoint がクラッシュする。
- 保存後の `Close` メソッド:
`Presentation.Save` 直後に `Presentation.Close` を呼ぶ際、保存処理が完了する前に制御が戻ることがある(特にネットワークドライブ)。保存後は `DoEvents` を挟み、ファイルシステムへの書き込みコミットを待機させるのが極意だ。
- COMインターフェースの解放:
`Set pres = Nothing` をループの最後で実行するのは当然だが、`Application` オブジェクト自体を長期間保持し続けるのも危険だ。定数回数の処理ごとに一度インスタンスを再起動する「リフレッシュ戦略」を採ることで、VBAのメモリリークを物理的に回避できる。
—
結論:自動化の真髄は「失敗を前提とした設計」にある
ネットワーク環境下で完璧な動作を期待してはいけない。システム管理の現場において、VBAコードに求められるのは「いかに賢く失敗し、いかに華麗にリトライするか」という耐障害性そのものだ。
今回紹介したAPIによる事前チェックとリトライループは、単なるテクニックではない。不安定なインフラ環境とレガシーなOfficeアプリケーションを繋ぐ、唯一の信頼できるブリッジなのだ。
さあ、このコードをあなたの武器に加え、不安定な共有ファイル環境を完全な制御下に置くがいい。健闘を祈る。
