ネットワークの深淵に抗う:PowerPoint VBAによる「絶対失敗を許さない」ファイルI/O戦術
世の「VBA入門」を謳う記事の多くは、ローカルデスクトップという極めて平穏な環境での動作を前提としている。しかし、我々プロフェッショナルが対峙する現場はもっと過酷だ。
不安定なVPN、遅延の激しい海外拠点のファイルサーバー、そして気まぐれに発生するSMBプロトコルのタイムアウト。こうした悪条件下で、標準的な `ActivePresentation.SaveAs` を無策に叩けば、待っているのは「実行時エラー ‘1004’」という無慈悲な停止である。
業務自動化エンジニアとして、あなたの書くコードは「動けばいい」レベルであってはならない。「いかなるネットワークの揺らぎさえも、再試行と待機戦略によってねじ伏せ、確実に成果物をデリバリーする」。そのための堅牢なアーキテクチャを、今ここで提示しよう。
—
1. なぜ「普通の保存」はネットワークで死ぬのか
ネットワーク越し(UNCパス)のファイル操作が失敗する理由は、単なる「切断」だけではない。
1. ファイルロックの伝播遅延: 直前に作成したフォルダが、サーバー側で「準備完了」になる前に保存命令が届く。
2. アンチウイルスの介入: ファイル書き込みの瞬間にサーバー側のスキャンが走り、I/Oが排他的にロックされる。
3. 指数関数的な応答劣化: ネットワーク負荷が高い際、単純なループでリトライを繰り返すと、自らサーバーにDoS攻撃を仕掛ける形になり、状況を悪化させる。
これらを克服するには、「エクスポネンシャル・バックオフ(指数関数的後退)」と「ステート確認の徹底」が必要不可欠だ。
—
2. 堅牢なネットワークI/Oを司る「RobustSave」エンジンの設計
以下のコードは、単なる保存処理を「ミッションクリティカルなタスク」へと昇華させるためのプロトタイプだ。
実装のポイント:
- 指数関数的バックオフ: 失敗するたびに待ち時間を 1s, 2s, 4s… と倍増させ、ネットワークの回復を待つ。
- DoEventsの戦略的配置: OSのメッセージキューを捌き、COMコンポーネントのハングアップを防ぐ。
- UNCパスの事前検証: 保存前にディレクトリの存在をFSOで確認し、一時的な瞬断を切り分ける。
Option Explicit
‘ Windows API: 高精度なスリープ制御
If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As LongPtr)
Else
Private Declare Sub Sleep Lib “kernel32″ (ByVal dwMilliseconds As Long)
End If
”’
”’
”’ 対象プレゼンテーションオブジェクト
”’ 保存先のフルパス(UNC推奨)
”’ 保存形式(ppSaveAsPDF等)
Public Function SafeNetworkExport(ByRef pres As Object, _
ByVal targetPath As String, _
Optional ByVal formatType As Long = 32) As Boolean ‘ 32 = ppSaveAsPDF
Dim maxRetries As Integer: maxRetries = 5
Dim retryCount As Integer: retryCount = 0
Dim waitTime As Long: waitTime = 1000 ‘ 初期待機時間 1秒
Dim success As Boolean: success = False
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ 保存先ディレクトリの存在確認(ネットワーク瞬断を考慮し、ここでもリトライをかけるのが定石)
Dim folderPath As String
folderPath = fso.GetParentFolderName(targetPath)
Do While retryCount < maxRetries
On Error Resume Next
' 1. ディレクトリの導通確認(SMBセッションの活性化)
If Not fso.FolderExists(folderPath) Then
Err.Raise 500, , "Network path unreachable"
End If
' 2. 保存実行
' Export As Fixed Format (PDF) または SaveAs を切り替え
If formatType = 32 Then ' ppSaveAsPDF
pres.ExportAsFixedFormat _
Path:=targetPath, _
FixedFormatType:=2, _ ' ppFixedFormatTypePDF
Intent:=2, _ ' ppFixedFormatIntentPrint
FrameSlides:=1 ' msoTrue
Else
pres.SaveAs targetPath, formatType
End If
If Err.Number = 0 Then
success = True
Exit Do
Else
' エラー発生時のログ出力(実運用ではイミディエイトウィンドウではなくログファイルへ)
Debug.Print "Attempt " & (retryCount + 1) & " failed: " & Err.Description
retryCount = retryCount + 1
' 指数関数的バックオフ:1s -> 2s -> 4s -> 8s…
DoEvents
Sleep waitTime
waitTime = waitTime 2
DoEvents
End If
On Error GoTo 0
Loop
Set fso = Nothing
SafeNetworkExport = success
End Function
—
3. 実務で「差が出る」アーキテクチャの急所
① `FileSystemObject` を捨てない理由
VBA標準の `Dir()` 関数は、ネットワークパスが不安定な際に「空文字」を返すのか「エラー」を吐くのかがOSのバージョンやSMBの挙動に依存し、極めて不安定だ。一方で `Scripting.FileSystemObject` は、COMのレイヤーで適切にエラーをハンドリングできるため、ネットワーク越しの存在確認にはこちらを標準採用すべきである。
② PDF変換時の「ゴーストプロセス」対策
PowerPointのPDF変換(`ExportAsFixedFormat`)は、内部的に仮想プリンタエンジンを呼び出すことがある。ネットワークが遅いと、このプロセスが非同期的に残り続け、次のファイルの保存をブロックすることがある。
上記のコードで `DoEvents` を `Sleep` の前後に配置しているのは、単なるおまじないではない。OSレベルでの描画更新と、COMのメッセージバッファをクリアし、プロセスがデッドロックに陥るのを防ぐための「儀式」だ。
③ データベース連携時の「整合性」の担保
もし保存完了後に、保存パスをデータベース(SQL ServerやAccess)に書き込む設計にするならば、必ず 「ファイルの実在確認」 をトランザクションのトリガーにせよ。
「SaveAsメソッドが正常終了した = ファイルがディスクに書き込まれた」と過信してはならない。サーバー側の遅延書き込み(Write-behind caching)により、VBAが制御を戻した瞬間にはまだファイルが「0バイト」である可能性がある。
‘ 保存後の実在・サイズ確認
If fso.FileExists(targetPath) Then
If fso.GetFile(targetPath).Size > 0 Then
‘ ここで初めてDB更新などの後続処理へ
End If
End If
—
4. 終わりに:ツールを「作品」へと昇華させる
多くの開発者は、正常系のコードを書くことに心血を注ぐ。しかし、チーフアーキテクトとしての評価を分けるのは、異常系に対する「想像力」だ。
「サーバーがメンテナンス中だったら?」「VPNがトンネル再構築に入ったら?」「ユーザーが保存中にLANケーブルを抜いたら?」
これらの問いに対し、無言で数秒間リトライし、何事もなかったかのように業務を完遂させるコード。それこそが、現場が求めている真の自動化である。
このスクリプトをあなたのライブラリに組み込み、ネットワークという不確定要素を完全にコントロール下に置いてほしい。技術の真髄は、常に細部に、そして「最悪の事態への備え」の中に宿るのだから。
