【極限設計】単一スレッドの呪縛を解け:マルチプロセス駆動によるPPTX一括PDF変換・同期エンジンの構築
「VBAで数百個のファイルを順番に処理させていたら、昼休みが終わってしまった」
もし君が、このような初歩的な設計ミスで時間を浪費しているとしたら、それはエンジニアとして致命的だ。PowerPointのVBAは、その性質上、単一スレッドで動作する。つまり、1つのインスタンスが1つのタスクをこなしている間、他のCPUコアは退屈なほど暇を決め込んでいるのだ。
現代のワークステーションは、8コア、16コア、あるいはそれ以上のマルチコアCPUを搭載している。これを使わずに、一本道のシーケンシャルな処理を強いるのは、F1マシンで渋滞の一般道を走るようなものだ。
本記事では、VBAの単一スレッドという「物理的限界」を、VBScriptによるマルチプロセス制御によって突破する。数百規模のPPTXファイルを、マルチコアをフル活用して並列・超高速にPDF変換・クラウド同期させる、プロフェッショナル向けの「並列バッチ基盤」の設計思想と実装を伝授しよう。
1. 設計思想:Master-Workerアーキテクチャの導入
単一のVBAマクロでループを回す設計を捨てろ。我々が構築するのは、「指揮官(Master)」と「実行兵(Worker)」を分離したMaster-Workerアーキテクチャだ。
アーキテクチャの構造
1. Master (VBScript/PowerShell):
- 対象となるファイルのリストを取得し、タスクをキュー(Queue)化する。
- CPUのコア数に基づき、複数の「Workerプロセス」を同時起動する。
- 各Workerの生存確認と、終了後のリソース回収を行う。
2. Worker (VBScript + PowerPoint COM):
- Masterから渡された「単一のファイルパス」を、独立したPowerPointインスタンスで開く。
- PDFエクスポートを実行し、成功/失敗のステータスを返す。
- 処理が終われば、自身を完全に終了(Terminate)させる。
この設計の肝は、「PowerPointのインスタンスをプロセスレベルで分離する」点にある。これにより、OSレベルでの並列処理が可能になり、マルチコアのパワーをダイレクトにスループットへ変換できる。
2. 実装:プロフェッショナル・コード・エディション
ここでは、実務に耐えうる堅牢なコードを示す。
【Worker】単一ファイル処理エンジン (`Worker.vbs`)
このスクリプトは、引数として受け取った1つのファイルを処理する「最小単位」だ。
' ==========================================================================
' Worker.vbs
' 役割: 単一のPPTXをPDFに変換し、クリーンアップを行う
' 使用方法: cscript Worker.vbs "C:\Path\To\File.pptx"
' ==========================================================================
Option Explicit
Dim objArgs, strFilePath, strPdfPath
Set objArgs = WScript.Arguments
' 引数チェック
If objArgs.Count < 1 Then
WScript.Echo "Error: No file path provided."
WScript.Quit 1
End If
strFilePath = objArgs(0)
strPdfPath = Replace(strFilePath, ".pptx", ".pdf")
Dim objPPT, objPres
Set objPPT = Nothing
Set objPres = Nothing
On Error Resume Next ' エラーハンドリングの開始
' PowerPointインスタンスの生成(独立したプロセスとして起動)
Set objPPT = CreateObject("PowerPoint.Application")
' プレゼンテーションを開く
' ReadOnly:=True, Untitled:=False, WithWindow:=False (ウィンドウ非表示)
Set objPres = objPPT.Presentations.Open(strFilePath, -1, 0, 0)
If Err.Number <> 0 Then
WScript.Echo "Error opening file: " & strFilePath & " | " & Err.Description
If Not objPPT Is Nothing Then objPPT.Quit
WScript.Quit 1
End If
' PDFエクスポート実行 (ppSaveAsPDF = 32)
' 成功したら終了コード0を返す
objPres.SaveAs strPdfPath, 32
If Err.Number <> 0 Then
WScript.Echo "Error exporting PDF: " & strFilePath & " | " & Err.Description
objPres.Close
objPPT.Quit
WScript.Quit 1
Else
objPres.Close
objPPT.Quit
WScript.Quit 0 ' 成功
End If
On Error GoTo 0
【Master】並列プロセス・オーケストレーター (`Master.vbs`)
これが全体の司令塔だ。CPUコア数に合わせて、Workerを並列に走らせる。
' ==========================================================================
' Master.vbs
' 役割: ファイルリストを管理し、複数のWorkerを並列起動する
' ==========================================================================
Option Explicit
Dim objFSO, objFolder, strTargetDir, strWorkerPath
Dim colFiles, objFile, strFile, i
Dim maxWorkers, currentWorkers, objShell, objExec
' --- 設定領域 ---
strTargetDir = "C:\Work\Input\" ' 入力フォルダ
strWorkerPath = "C:\Work\Worker.vbs" ' Workerスクリプトのパス
maxWorkers = 4 ' 同時起動プロセス数(CPUコア数に合わせる)
' ----------------
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objShell = CreateObject("WScript.Shell")
If Not objFSO.FolderExists(strTargetDir) Then
WScript.Echo "Target directory not found."
WScript.Quit
End If
Set objFolder = objFSO.GetFolder(strTargetDir)
Set colFiles = objFolder.Files
' プロセス管理用のコレクション
Set currentWorkers = CreateObject("System.Collections.ArrayList")
WScript.Echo "Starting Parallel Processing Engine..."
' ファイルループ
For Each objFile In colFiles
If LCase(objFSO.GetExtensionName(objFile.Name)) = "pptx" Then
strFile = objFile.Path
' Workerの起動準備
' cscript を使用して、Workerを非同期プロセスとして起動
WScript.Echo "Queueing: " & objFile.Name
' プロセスが最大数に達している場合、空きが出るまで待機
Do While currentWorkers.Count >= maxWorkers
CheckWorkers currentWorkers, objShell
WScript.Sleep 1000 ' 1秒待機してCPU負荷を抑える
Loop
' Workerの起動
Set objExec = objShell.Exec("cscript //nologo """ & strWorkerPath & """ """ & strFile & """")
currentWorkers.Add objExec
End If
Next
' 全てのWorkerが終了するのを待機
WScript.Echo "All files queued. Waiting for remaining processes..."
Do While currentWorkers.Count > 0
CheckWorkers currentWorkers, objShell
WScript.Sleep 1000
Loop
WScript.Echo "Batch processing completed successfully."
' --- サブルーチン: プロセスの生存確認と回収 ---
Sub CheckWorkers(ByRef workerList, ByRef shell)
Dim j, objCurrent
For j = workerList.Count - 1 To 0 Step -1
Set objCurrent = workerList(j)
' ProcessIDが有効か、または終了しているか確認
' ExecオブジェクトのStatusを利用
If objCurrent.Status = 0 Then ' 0 = Running
' 実行中
Else
' 終了している
workerList.Remove j
End If
Next
End Sub
3. 運用における「極限の知見」:落とし穴を回避せよ
このシステムをプロダクション環境で動かす際、初心者が必ず陥る罠がある。アーキテクトとして、以下の3点を肝に銘じておけ。
① 「ゴースト・プロセス」の恐怖
PowerPointは、エラーが発生した際にCOMオブジェクトの解放に失敗し、`powerpnt.exe` がメモリ上に残り続けることが多々ある。これが積み重なると、メモリを食いつぶし、システム全体がハングアップする。
- 対策: Worker側では必ず `On Error Resume Next` を使い、エラー時でも `objPPT.Quit` を確実に通るように設計すること。また、Master側で一定時間応答のないプロセスを強制終了(`taskkill`)する監視ロジックを追加するのが理想的だ。
② ファイルロックと競合
クラウド同期(OneDriveやSharePoint)が走っているフォルダに対して、同時に大量の書き込みを行うと、ファイルロック(Sharing Violation)が発生する。
- 対策: 変換後のPDFは、同期対象外の「ローカル一時作業フォルダ」に出力し、全プロセス完了後に、一括で同期フォルダへ移動(Move)させる設計にせよ。これが最も堅牢だ。
③ リソースの過剰割り当て
「コア数=最大プロセス数」にするのが基本だが、PowerPointは意外とメモリを消費する。
- 対策: メモリ容量が少ない環境では、`maxWorkers` を `(物理コア数 / 2)` 程度に抑える。スループットよりも「完走すること」に重きを置くのが、プロフェッショナルの判断だ。
結論
VBAの限界は、言語の仕様ではなく、君の設計思想の限界だ。
単一スレッドの制約に縛られず、プロセスを切り出し、OSの力を借りる。この「抽象化」と「分散」の考え方こそが、単純な自動化担当者を、真の自動化エンジニアへと進化させる。
さあ、このコードをベースに、君の業務環境に合わせた最強のエンジンを組み上げてみせろ。
