共有フォルダの「死の淵」を越える:VBAによる堅牢なPDF一括変換アーキテクチャ
ネットワーク共有フォルダという名の戦場で、VBAを書くことは「綱渡り」に等しい。特に、複数人が同時にアクセスする環境でのファイル操作は、一歩間違えればメモリリーク、ゾンビプロセス、そして排他制御の欠如によるデータ破損を招く。
今日、我々が実装するのは、単なる「ファイル変換スクリプト」ではない。「他者が編集中であれば即座に検知し、安全に退避し、詳細なログと共に完遂する」、エンタープライズレベルの堅牢性を備えたシステムだ。
1. なぜ「共有フォルダ」でのVBAは苦戦するのか
共有ファイルには、Windowsのファイルシステムレベルのロック(`~$.pptx`のような一時ファイル)と、PowerPointアプリケーション層での読み取り専用モードの二重の壁がある。
安易に `Presentations.Open` を呼べば、読み取り専用ダイアログがポップアップし、自動化はそこで停止する。我々が求めるのは、「誰かが開いているなら、潔くスキップし、ログにその事実を刻む」という静かなる判断力だ。
2. 実装の核心:排他制御とAPIの活用
このスクリプトでは、以下の戦略を採る。
1. FSOでの事前チェック: ファイルのプロパティを調べ、アクセス可能か試行する。
2. APIによるメモリ管理: `DoEvents`の多用で逃げるのではなく、オブジェクトの参照を明示的に断ち切る。
3. エラーハンドリング: `Err.Number`を解析し、単なる変換エラーと「排他エラー」を峻別する。
堅牢なPDF一括変換スクリプト
Option Explicit
‘ メモリ保護のための明示的解放を徹底する
Public Sub BatchConvertPPTXToPDF(ByVal sourceFolder As String, ByVal outputFolder As String)
Dim fso As Object: Set fso = CreateObject(“Scripting.FileSystemObject”)
Dim pptApp As Object: Set pptApp = CreateObject(“PowerPoint.Application”)
Dim pptPres As Object
Dim folder As Object: Set folder = fso.GetFolder(sourceFolder)
Dim file As Object
On Error Resume Next
For Each file In folder.Files
If LCase(fso.GetExtensionName(file.Path)) = “pptx” Then
‘ 1. 排他制御チェック(書き込みテスト)
If IsFileLocked(file.Path) Then
LogAction “SKIP: ” & file.Name & ” は使用中のためスキップされました。”
Else
‘ 2. 安全なオープン(ReadOnly: True, WithWindow: False)
Set pptPres = pptApp.Presentations.Open(file.Path, , , False)
If Err.Number <> 0 Then
LogAction “ERROR: 開けません – ” & file.Name
Err.Clear
Else
‘ 3. PDF変換 (ppSaveAsPDF = 32)
pptPres.SaveAs outputFolder & “\” & fso.GetBaseName(file.Name) & “.pdf”, 32
pptPres.Close
LogAction “SUCCESS: ” & file.Name & ” を変換完了。”
End If
‘ オブジェクトの明示的解放(メモリリーク防止)
Set pptPres = Nothing
End If
End If
Next
pptApp.Quit
Set pptApp = Nothing
Set fso = Nothing
End Sub
‘ ファイルがロックされているかを確認する低レイヤー関数
Private Function IsFileLocked(filePath As String) As Boolean
Dim fileNum As Integer
On Error Resume Next
fileNum = FreeFile
Open filePath For Binary Access Read Lock Read As #fileNum
Close #fileNum
If Err.Number <> 0 Then IsFileLocked = True Else IsFileLocked = False
On Error GoTo 0
End Function
Private Sub LogAction(msg As String)
Debug.Print “[” & Now & “] ” & msg
‘ 実運用ではここにFSOでテキストファイルへの追記処理を入れること
End Sub
3. シニアエンジニアが意識すべき「深淵」
オブジェクトのライフサイクル
VBAの `Set = Nothing` は、単なるおまじないではない。特にPowerPointのCOMオブジェクトは、背後で重いプロセスを抱えている。`pptApp.Quit` を忘れると、タスクマネージャーにゾンビプロセスが蓄積し、やがて共有フォルダへのアクセス権限まで巻き込んでハングアップする。
ネットワークI/Oのボトルネック
共有フォルダ上のファイルを開く際、LANの帯域が細いと `Presentations.Open` はタイムアウトを起こしやすい。大規模なバッチ処理を行う場合は、一度ローカルの `Environ(“TEMP”)` にファイルをコピーしてから処理し、変換後に削除するという、「一時ローカルキャッシュ法」を導入するのが最も堅牢だ。
なぜ `WithWindow:=False` なのか
UIを表示させないことは、パフォーマンス向上だけでなく、「ユーザーの操作による予期せぬダイアログ表示」を回避する最大の防御策である。GUIを排したオートメーションこそが、サーバーサイドに肉薄するVBAの真骨頂だ。
最後に:コードは「生き物」である
この記事で提示したコードは、あくまで「骨子」である。実際の現場では、ファイル名の文字化け、PDF変換エンジン自体のフリーズ、ネットワークの瞬断など、予期せぬ事象が山積みとなるだろう。
しかし、この「排他制御」と「リソース解放」のアーキテクチャさえ身につけていれば、どんな現場でも「動かない」という悩みからは解放されるはずだ。
VBAはレガシーではない。使い手次第で、それは最強のオートメーション武器となる。あなたのコードに、魂を込めよ。
