【入門編】【上級者】マルチスレッド風の非同期タスク制御をVBScriptとのプロセス連携で擬似的に実現し、マルチコアCPUをフル活用して数百ファイルのPPTXを並列かつ超高速にPDFエクスポート・クラウド同期するバッチ基盤 – PowerPoint VBA解析バイブル

スポンサーリンク
スポンサーリンク

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が最後にまとめて報告する。

「動けばいい」というコードは誰でも書けます。しかし、「効率的で、堅牢で、スケーラブルな」システムを設計できるのが、真のプロフェッショナルです。

さあ、このコードをベースに、あなただけの最強の自動化エンジンを構築してみてください。応援していますよ!

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