【テクニカル・上級編】【上級者】ネットワークの遅延や切断を考慮し、UNCパス経由でのファイル入出力時に自動リトライとバックオフ制御を組み込んだ堅牢なネットワーク保存・PDF化スクリプト – PowerPoint VBA解析バイブル

スポンサーリンク

幽霊(ゴースト)との戦い:UNCパスにおけるPowerPoint I/Oの深淵

エンタープライズの現場において、VBAエンジニアを最も苦しめるのは言語仕様そのものではない。不安定なVPN、低帯域なWAN、そして気まぐれなファイルサーバーが引き起こす「非決定的なI/Oエラー」だ。

特にPowerPointは、Excelに比べてバイナリサイズが肥大化しやすく、`ExportAsFixedFormat`(PDF出力)などのメソッドは内部で複雑なスプーリング処理を行うため、ネットワークの瞬断に対して極めて脆弱である。

「保存に失敗しました」というダイアログをユーザーに見せるのは、プロフェッショナルとして三流だ。我々チーフアーキテクトが構築すべきは、一時的なネットワークの揺らぎを「無かったこと」にする、自己修復型のI/Oパイプラインである。

今回は、Windows APIを用いたネットワークステータスの監視と、指数バックオフ(Exponential Backoff)アルゴリズムを組み込んだ、極限まで堅牢なPDFエクスポート・ロジックを提示する。

—

1. アーキテクチャの鉄則:ネットワークへの直接書き込みを避けよ

まず、大前提を共有する。PowerPointのオブジェクトモデルから、直接UNCパス(`\\server\share\…`)に対してエクスポートを実行してはならない。

COMコンポーネントがネットワークI/Oのブロッキングを待機する際、タイムアウト制御はOSとCOMマーシャリングの挙動に依存し、VBA側から制御できない不透明なフリーズを招く。

正解は「Local-First, Sync-Later」だ。
1. ローカルの `%TEMP%` 領域にPDFを出力する。
2. Windows APIまたはFSOを用いて、リトライ制御を行いながらネットワークへ転送する。
3. 転送完了後、ローカルファイルを破棄する。

—

2. 堅牢性を担保するWindows API宣言

標準の `Dir` 関数や `FileSystemObject` は、ネットワーク遅延時に制御が戻ってこない、あるいは誤った戻り値を返すことがある。より低レイヤーのAPIを叩き、カーネルレベルの挙動を味方につける。

Option Explicit

‘ スレッドを一時停止し、CPUリソースを解放するためのAPI
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)

‘ パスがネットワーク上にあるか判定するAPI
Private Declare PtrSafe Function PathIsNetworkPath Lib “shlwapi.dll” Alias “PathIsNetworkPathA” (ByVal pszPath As String) As Long

‘ ファイル属性を取得し、接続性を確認するためのAPI
Private Declare PtrSafe Function GetFileAttributes Lib “kernel32” Alias “GetFileAttributesA” (ByVal lpFileName As String) As Long

Private Const INVALID_FILE_ATTRIBUTES As Long = -1

—

3. 指数バックオフによる自動リトライ・ロジック

エラー発生時、即座にリトライするのは愚策だ。ネットワークの輻輳(ふくそう)を悪化させるだけである。待機時間を `2^n` で増やしていく指数バックオフが、分散システムにおける黄金律である。

”’

”’ ネットワーク上の保存先に対して、リトライ制御付きでPDFを転送する
”’

”’ 対象プレゼンテーションオブジェクト ”’ 最終的なUNC保存パス Public Sub RobustExportToPDF(ByRef pres As PowerPoint.Presentation, ByVal uncPath As String)
Dim localTempPath As String
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ 1. ローカル一時パスの生成
localTempPath = Environ(“TEMP”) & “\” & fso.GetTempName() & “.pdf”

On Error GoTo ErrorHandler

‘ 2. ローカルへの出力(ここは高速かつ安定)
Debug.Print “Exporting to local temp: ” & localTempPath
pres.ExportAsFixedFormat _
Path:=localTempPath, _
FixedFormatType:=ppFixedFormatTypePDF, _
Intent:=ppFixedFormatIntentPrint

‘ 3. ネットワーク転送(リトライ制御フェーズ)
If Not CopyFileWithRetry(localTempPath, uncPath) Then
Err.Raise 1001, “RobustExport”, “ネットワーク転送に致命的な失敗をしました。”
End If

‘ 4. 後処理
If fso.FileExists(localTempPath) Then fso.DeleteFile localTempPath

Exit Sub

ErrorHandler:
Dim errDesc As String: errDesc = Err.Description
‘ オブジェクトの安全な解放
If Not fso Is Nothing Then
If fso.FileExists(localTempPath) Then fso.DeleteFile localTempPath
End If
MsgBox “致命的エラー: ” & errDesc, vbCritical
End Sub

”’

”’ 指数バックオフを用いたファイルコピー
”’

Private Function CopyFileWithRetry(ByVal src As String, ByVal dest As String) As Boolean
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)

Dim maxRetries As Integer: maxRetries = 5
Dim attempt As Integer: attempt = 0
Dim waitTime As Long: waitTime = 1000 ‘ 初回1秒

Do While attempt < maxRetries On Error Resume Next ' 宛先ディレクトリの存在確認(APIレベル) If GetFileAttributes(fso.GetParentFolderName(dest)) = INVALID_FILE_ATTRIBUTES Then ' ネットワークパスが見えない場合は意図的にエラーを発生させてリトライへ Err.Raise 76 End If ' コピー実行(既存ファイルは上書き) fso.CopyFile src, dest, True If Err.Number = 0 Then CopyFileWithRetry = True Exit Function End If ' エラー発生時の処理 Debug.Print "Attempt " & attempt & " failed: " & Err.Description attempt = attempt + 1 If attempt < maxRetries Then ' 指数バックオフ: 1s, 2s, 4s, 8s, 16s... と待機時間を増やす Debug.Print "Retrying in " & waitTime & "ms..." Sleep waitTime waitTime = waitTime 2 DoEvents ' UIフリーズ防止 End If On Error GoTo 0 Loop CopyFileWithRetry = False End Function ---

4. 知見:PowerPoint COMオブジェクトの「静かな死」を防ぐ

上記のコードを運用する際、上級者が留意すべきは PowerPoint Application のインスタンス管理だ。

1. DoEvents の副作用: `Sleep` 中やリトライループ内で `DoEvents` を呼ぶと、ユーザーがPowerPointを操作できてしまう。バッチ処理中は `Application.ActiveWindow.ViewType` をチェックするか、透明なモーダルフォームを表示して操作をブロックする等の配慮が必要だ。
2. ゾンビプロセスの防止: ネットワークエラーでスクリプトが中断された際、`PowerPoint.Application` オブジェクトがメモリに残ることがある。必ず `App.Quit` と `Set App = Nothing` を `Finally` 相当のブロックで行うこと。
3. PDF出力の非同期性: `ExportAsFixedFormat` はメソッドが戻ってきた直後でも、OSレベルでファイルハンドルがロックされている場合がある。ネットワークコピーの前に `GetFileAttributes` でファイルが完全に書き込まれたか確認するループを挟むのが、真のプロの仕事だ。

結論

ネットワークI/Oを伴う自動化において、VBA標準機能のみを信じるのは「設計の放棄」に等しい。
ローカルバッファリング、Windows APIによる状態監視、そして指数バックオフ。これらを組み合わせることで初めて、エンタープライズの過酷な環境に耐えうる「伝説的な」ツールへと昇華する。

レガシーな技術であっても、その背後にあるアーキテクチャ思考がモダンであれば、生み出される価値は決して古びることはない。

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