PowerPoint VBAの限界を超えろ:マルチプロセスによる「超並列」PDF変換エンジンの構築
こんにちは。自動化の世界へようこそ。
マクロの記録ボタンを押し、生成されたコードを見て「ふむふむ、なるほど」と納得できるようになったあなた。素晴らしい一歩です。でも、もしあなたが「数百枚のプレゼンテーションを、一瞬でPDFに変換してクラウドに同期したい」と願ったとき、一つの壁にぶつかるはずです。
それは、「待ち時間」という名の絶望です。
「一つ終わるまで、次のファイルには進めない……」
「CPUはこんなに空いているのに、PowerPointは一人で一生懸命作業している……」
今日は、そんな「単一スレッド(一列に並んで作業する状態)」の限界を突破し、マルチコアCPUの力をフルに引き出す「擬似マルチスレッド・並列処理エンジン」の設計思想と実装方法を伝授します。
これは単なるマクロの延長ではありません。「プロセス制御」というエンジニアリングの領域への招待状です。
1. なぜ「通常のVBA」では遅いのか?
まず、敵を知ることから始めましょう。
通常のVBAで `For Each file In files` とループを回してPDF保存を行う場合、プログラムの動きはこうなります。
1. PowerPointを起動する
2. ファイルAを開く $\rightarrow$ PDF保存 $\rightarrow$ 閉じる
3. ファイルBを開く $\rightarrow$ PDF保存 $\rightarrow$ 閉じる
4. …(これを繰り返す)
この方式は「シングルスレッド」と呼ばれます。たとえあなたのPCが16コアのモンスターマシンであっても、PowerPointという「一人の作業員」が順番にこなしていくため、残りの15コアはただの置物になってしまいます。
私たちが目指すのは、「複数の作業員(プロセス)を同時に召喚し、仕事を分担させる」という戦略です。
2. 解決策:マスター・ワーカー・アーキテクチャ
VBA単体では、自分自身を「並列に走らせる」ことはできません。そこで、VBScript(Windows標準のスクリプト)を司令塔(マスター)として使い、複数のPowerPointプロセス(ワーカー)を同時に立ち上げる手法をとります。
アーキテクチャ図解
[ Master Script (VBScript) ] <-- 全体の司令塔
|-- 分割命令1 --> [ Worker Process 1 (PowerPoint Instance A) ] --> ファイル1〜50を処理
|-- 分割命令2 --> [ Worker Process 2 (PowerPoint Instance B) ] --> ファイル51〜100を処理
|-- 分割命令3 --> [ Worker Process 3 (PowerPoint Instance C) ] --> ファイル101〜150を処理
└-- 全完了を検知して終了
このように、仕事を「塊(チャンク)」に分けて、複数のプロセスに投げます。これが、VBAの限界を超える「擬似マルチスレッド」の正体です。
3. 実践:超高速PDF変換エンジンの実装
では、実際に動くコードを見ていきましょう。今回は2つのファイルに分けて実装します。
① ワーカー(Worker.vbs)
このスクリプトは、渡された「ファイルリストの断片」を処理する実務部隊です。VBAのオブジェクトモデルを直接操作します。
' ==========================================================
' Worker.vbs
' 特定のファイルリストを受け取り、PDFへエクスポートする実務部隊
' ==========================================================
Option Explicit
Dim objPPT, objArgs, filePath, targetFolder
Set objArgs = WScript.Arguments
' 引数チェック:ファイルパスが渡されていない場合は終了
If objArgs.Count = 0 Then
WScript.Quit
End If
' PowerPointのインスタンスを作成(新しいプロセスとして起動)
On Error Resume Next
Set objPPT = CreateObject("PowerPoint.Application")
' 画面を表示させない(高速化のため)
' objPPT.Visible = True ' デバッグ時はTrueにすると動きが見えます
' 引数として渡されたファイルリストを順次処理
Dim i
For i = 0 To objArgs.Count - 1
filePath = objArgs(i)
' ファイルが存在するか確認
Dim fso: Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FileExists(filePath) Then
Dim pres, pdfPath
' プレゼンテーションを開く
Set pres = objPPT.Presentations.Open(filePath, , , False)
' 保存先のパスを作成(拡張子を.pdfに変更)
pdfPath = fso.GetParentFolderName(filePath) & "\" & _
fso.GetBaseName(filePath) & ".pdf"
' PDFとして保存 (ppSaveAsPDF = 32)
pres.SaveAs pdfPath, 32
' プレゼンテーションを閉じる(メモリ解放が重要!)
pres.Close
Set pres = Nothing
End If
Next
' PowerPointを終了してプロセスを解放
objPPT.Quit
Set objPPT = Nothing
WScript.Quit
② マスター(Master.vbs)
これが司令塔です。全ファイルをスキャンし、CPUコア数に合わせてワーカーを並列起動します。
' ==========================================================
' Master.vbs
' ファイルを分割し、複数のWorker.vbsを並列起動する司令塔
' ==========================================================
Option Explicit
Dim objShell, fso, folder, file, fileList
Dim workerScript, maxProcesses, i, chunkCount
Dim targetDir, currentFile
' --- 設定エリア ---
targetDir = "C:\Your\PPTX\Folder" ' 対象フォルダ
workerScript = "C:\Your\Scripts\Worker.vbs" ' Workerのパス
maxProcesses = 4 ' 同時に立ち上げるPowerPointの数(CPUコア数に合わせる)
' ----------------
Set fso = CreateObject("Scripting.FileSystemObject")
Set objShell = CreateObject("WScript.Shell")
Set fileList = CreateObject("System.Collections.ArrayList")
' 1. 対象フォルダ内のPPTXファイルを収集
If Not fso.FolderExists(targetDir) Then
WScript.Echo "Error: Target directory not found."
WScript.Quit
End If
Set folder = fso.GetFolder(targetDir)
For Each file In folder.Files
If LCase(fso.GetExtensionName(file.Name)) = "pptx" Then
fileList.Add file.Path
End If
Next
If fileList.Count = 0 Then
WScript.Echo "No PPTX files found."
WScript.Quit
End If
' 2. 並列処理の実行
WScript.Echo "Starting parallel processing with " & maxProcesses & " workers..."
Dim totalFiles: totalFiles = fileList.Count
Dim filesPerWorker: filesPerWorker = Int(totalFiles / maxProcesses) + 1
Dim currentIdx: currentIdx = 0
Dim processCount: processCount = 0
' 3. ワーカーを順次投入
Do While currentIdx < totalFiles
Dim cmd, args
args = ""
' 1つのワーカーに渡すファイルリストを作成
Dim j
For j = 0 To filesPerWorker - 1
If currentIdx < totalFiles Then
args = args & """" & fileList(currentIdx) & """ "
currentIdx = currentIdx + 1
End If
Next
' Worker.vbs を「非同期(WaitOnReturn=False)」で起動
' これにより、Workerの終了を待たずに次のWorkerを起動できる
cmd = "wscript.exe """ & workerScript & """ " & args
objShell.Run cmd, 0, False
processCount = processCount + 1
' コア数に達したら、少し待機するか、あるいはシンプルに全て投げ切る
' ※メモリ不足が心配な場合は、ここでプロセスの終了を監視するロジックを追加します
Loop
WScript.Echo "All workers dispatched. Check your folder for PDF files."
4. プロフェッショナルが陥る「落とし穴」と回避術
この仕組みは強力ですが、無計画に使うとシステムを破壊します。以下の3点は、必ず守ってください。
① メモリの爆発(Memory Exhaustion)
PowerPointは非常に「重い」アプリケーションです。`maxProcesses = 16` など、CPUコア数に合わせて無闇に増やしすぎると、メモリが枯渇してPCがフリーズします。
「実用的な並列数は、物理コア数の半分程度」からテストを始めるのが、賢明なエンジニアの作法です。
② オブジェクトのライフサイクル管理
`Worker.vbs` 内で、`pres.Close` と `objPPT.Quit` を忘れてはいけません。これを忘れると、バックグラウンドに「幽霊プロセス(ゾンビプロセス)」が大量に残り、メモリを食いつぶし続けます。
③ ファイルロックの競合
もし、複数のワーカーが「同じファイル」を読み込もうとしたり、同じ場所に書き込もうとしたりすると、エラーが発生します。ファイルリストを分割する際は、必ず「一意のファイル群」になるよう設計してください。
5. まとめ:次なるステップへ
お疲れ様でした。あなたは今、単なる「マクロ使い」から、「プロセスを操る自動化エンジニア」への階段を登り始めました。
今回の手法を応用すれば、以下のようなことも可能になります。
- クラウド同期の統合: PDF変換直後に、`Shell.Run` で `rclone` や `aws s3 cp` を呼び出し、即座にクラウドへアップロードする。
- エラーログの集約: Workerがエラーを出した際、テキストファイルに書き出し、Masterが最後にまとめて報告する。
「動けばいい」というコードは誰でも書けます。しかし、「効率的で、堅牢で、スケーラブルな」システムを設計できるのが、真のプロフェッショナルです。
さあ、このコードをベースに、あなただけの最強の自動化エンジンを構築してみてください。応援していますよ!
