【入門編】【中級者】FSO(FileSystemObject)を駆使し、指定フォルダ内のサブフォルダ構造を完全維持したまま、すべてのPPTXをPDFに変換・配置する階層維持エクスポートマクロ – PowerPoint VBA解析バイブル

スポンサーリンク

こんにちは。PowerPoint VBAの世界へようこそ。
マクロの記録ボタンを押すだけのステージから抜け出し、「自分の手で業務を自在にコントロールしたい」と願うあなたにとって、今日は記念すべき日になるはずです。

実務の世界では、「デスクトップに散らばった大量のPowerPointファイルを、元のフォルダ階層を完璧に維持したままPDFに変換してほしい」といった、一見すると気が遠くなるような依頼が日常茶飯事に舞い込んできます。

手作業でフォルダを作り、一つずつファイルを開いて「名前を付けて保存」を繰り返していたら、それだけで貴重な1日が終わってしまいますよね。

今回は、Windowsのファイルシステムを縦横無尽に操る「FSO(FileSystemObject)」と、PowerPointの裏側を支配する「再帰処理(Recursive Processing)」を組み合わせた、実戦でそのまま使える極限の自動化スクリプトを授けましょう。

ここをクリアすれば、あなたのVBAスキルは間違いなく中級者の領域へと突入します。さあ、一緒にコードの深淵へ足を踏み入れましょう。

—

1. なぜ「FSO + 再帰処理」が必要なのか?

フォルダの中に別のフォルダがあり、さらにその中にフォルダがある……という、いわゆる「入れ子構造(階層構造)」をプログラムで処理するのは、一昔前までは非常に面倒でした。

ここで登場するのが `Scripting.FileSystemObject`(通称:FSO) です。
FSOを使えば、OSのファイルやフォルダをまるで自分の手足のように自由自在に取得・作成・操作できるようになります。

さらに、フォルダ階層の深さが「何階層であっても対応できる」ようにするための魔法が、自分自身を呼び出す関数である「再帰(さいき)処理」です。

[親フォルダ]
┣ 企画書.pptx ──> [変換!]
┗ [A案件]
┣ 提案書.pptx ──> [変換!]
┗ [資料]
┗ 詳細.pptx ──> [変換!]

この複雑な構造を、コードはたった一つのルール(「フォルダを見つけたら、中身を同じ要領で調べる」)だけで完璧に踏破します。

—

2. 開発の現場で即戦力となる全自動スクリプト

それでは、今回の主役となるコードを公開します。
VBAのエディタを開き、標準モジュールに以下のコードを貼り付けてください。

※実行には、あらかじめMicrosoft Scripting Runtimeへの参照設定を行うか、遅延バインディング(今回はより安全な参照設定を前提とした記述にしています)を使用します。

Option Explicit

‘ =====================================================================
‘ 処理名: フォルダ階層を完全維持したままPPTXをPDFに一括変換
‘ 概要: 指定された入力元のフォルダを再帰的に探索し、出力先に
‘ 同じフォルダ構造を再現しながら全PPTXをPDF化して配置する。
‘ =====================================================================
Sub ExportPowerPointToPdfRecursive()
Dim fso As Object
Dim sourcePath As String, destPath As String

‘ 1. FSOのインスタンス生成
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ 2. 入力元と出力先のパスを指定(必要に応じて書き換えてください)
‘ ※末尾の「\(バックスラッシュ)」の有無に注意してください
sourcePath = “C:\Work\Presentation_Source”
destPath = “C:\Work\Presentation_Output”

‘ 3. 事前チェック
If Not fso.FolderExists(sourcePath) Then
MsgBox “指定された入力元フォルダが存在しません:観測パス=” & sourcePath, vbCritical, “エラー”
Exit Sub
End If

‘ 4. 出力先ルートフォルダがなければ作成
If Not fso.FolderExists(destPath) Then
fso.CreateFolder (destPath)
End If

‘ 5. 処理の核心(再帰関数の呼び出し)
Dim startTime As Double
startTime = Timer

Call ProcessFolder(fso, fso.GetFolder(sourcePath), sourcePath, destPath)

MsgBox “すべてのPDF変換が完了しました!” & vbCrLf & _
“処理時間: ” & Format(Timer – startTime, “0.00”) & ” 秒”, vbInformation, “完了”

Set fso = Nothing
End Sub

‘ =====================================================================
‘ 関数名: ProcessFolder (再帰処理コアエンジン)
‘ 引数: fso – FileSystemObject
‘ currentFolder – 現在走査中のFolderオブジェクト
‘ srcRoot – 入力元のルートパス
‘ dstRoot – 出力先のルートパス
‘ =====================================================================
Private Sub ProcessFolder(fso As Object, currentFolder As Object, srcRoot As String, dstRoot As String)
Dim subFolder As Object
Dim file As Object
Dim targetPdfPath As String
Dim pptApp As Object
Dim pptPres As Object

‘ 出力側での対応するフォルダパスを算出
‘ 例: C:\Work\Source\A\B ──> C:\Work\Output\A\B を導き出す
Dim relativePath As String
Dim currentDestPath As String

relativePath = Mid(currentFolder.Path, Len(srcRoot) + 1)
currentDestPath = dstRoot & relativePath

‘ 出力側に同名フォルダが存在しなければ作成
If Not fso.FolderExists(currentDestPath) Then
fso.CreateFolder (currentDestPath)
End If

‘ — A. 現在のフォルダ内にあるすべてのPPTXファイルを処理 —
For Each file In currentFolder.Files
‘ 拡張子が .pptx または .ppt のファイルを対象とする(一時ファイル “~$” は除外)
If (LCase(fso.GetExtensionName(file.Name)) = “pptx” Or LCase(fso.GetExtensionName(file.Name)) = “ppt”) And _
Left(file.Name, 2) <> “~$” Then

‘ 出力先のPDFパスを構築(拡張子を .pdf に置換)
targetPdfPath = currentDestPath & “\” & fso.GetBaseName(file.Name) & “.pdf”

‘ すでにPDFが存在する場合はスキップするか上書きするか(今回は上書き保存)
On Error Resume Next

‘ PowerPointのバックグラウンド起動による変換処理
Set pptApp = CreateObject(“PowerPoint.Application”)
‘ ※PowerPointのウィンドウを表示させずに高速処理したい場合は .Visible = msoFalse にしますが
‘ 環境によって不安定になるため、最小化状態で安全に処理します
pptApp.Visible = msoTrue
pptApp.WindowState = 2 ‘ 最小化

Set pptPres = pptApp.Presentations.Open(file.Path, ReadOnly:=True, WithWindow:=False)

‘ PowerPointの標準機能であるSaveAsでPDF出力 (ファイル形式定数: ppSaveAsPDF = 32)
pptPres.SaveAs targetPdfPath, 32

pptPres.Close
pptApp.Quit

Set pptPres = Nothing
Set pptApp = Nothing
On Error GoTo 0
End If
Next file

‘ — B. サブフォルダを検出し、自分自身を再度呼び出す(再帰) —
For Each subFolder In currentFolder.SubFolders
Call ProcessFolder(fso, subFolder, srcRoot, dstRoot)
Next subFolder
End Sub

—

3. コードのキモを徹底解説!プロの着眼点

このコードがなぜ優れているのか、中級者へのステップアップとして押さえておいてほしいポイントを3つ解説します。

① `Mid`関数を使った「相対パス」の巧妙な抽出

サブフォルダをコピーして再作成する際、最も頭を悩ませるのが「出力先での正しい保存先パスの特定」です。
ここでは、現在のフォルダの絶対パスから入力元のルートパスの文字数を引いた「相対パス(`relativePath`)」を抽出し、出力先のルートパスと結合するという手法をとっています。これにより、何階層下に潜ろうとも、出力側に寸分の狂いもなく同じツリー構造を再現できます。

② 一時ファイル(`~$`)の華麗なるスリップ

WindowsでPowerPointを開いている最中、同じフォルダ内には必ず `~$プレゼン資料.pptx` といった隠しファイル(ロックファイル)が自動生成されます。
これをうっかり処理対象にしてしまうと、「ファイルを開けません」という致命的なエラーでマクロがクラッシュします。
条件分岐に `Left(file.Name, 2) <> “~$”` を組み込むことで、こうした実務特有のトラブルをスマートに回避しています。

③ PowerPointオブジェクトのライフサイクル管理

ループの中で何度もPowerPointを立ち上げたり閉じたりする処理は、メモリリーク(メモリの解放漏れ)を引き起こしやすい危険なゾーンです。
このコードでは、1ファイル処理するごとに `pptPres.Close` と `pptApp.Quit` を確実に実行し、変数を `Nothing` にクリアすることで、何百枚ものファイルを処理しても動作が重くならない堅牢な設計にしています。

—

4. 陥りやすい罠とエラー対策

現場でこのマクロを動かしたとき、次のような壁にぶぶつかることがあります。あらかじめ処方箋を知っておきましょう。

  • エラー:「コンパイルエラー: ユーザー定義型は定義されていません」
  • 原因: FSO(`Scripting.FileSystemObject`)を使用するための参照設定が外れているか、コード内の記述に誤りがあります。今回は `CreateObject(“Scripting.FileSystemObject”)` という「遅延バインディング」を採用しているため、原則として参照設定のチェックは不要ですが、もし型名で宣言している場合は注意してください。
  • エラー:「ファイルを開けません(または読み取り専用で開いています)」
  • 原因: 誰かがそのファイルを開いている、あるいはネットワークドライブ上のファイルの同期ラグが原因です。`ReadOnly:=True` を指定して安全に開くよう対策していますが、極端に重いファイルやパスの文字数が長すぎる(Windowsの260文字制限)場合にも発生します。保存先フォルダのパスはできるだけ浅い階層(例: `C:\Work` など)に設定するのが鉄則です。

—

まとめ:ここをクリアすれば、もう怖くない!

フォルダの階層構造を維持したまま一括処理するロジックは、Excel VBAでもPowerPoint VBAでも、業務自動化の最高峰に位置するテクニックの一つです。

「FSOによるファイル走査」と「再帰処理による階層の突破」、そして「安全なオブジェクトの破棄」。
この3つをマスターしたあなたなら、今後どんなに膨大なファイル整理の依頼が来ても、涼しい顔で数秒のうちに終わらせてしまうことができるでしょう。

ここをクリアしたあなたは、もう立派な自動化エンジニアの仲間入りです。
ぜひ日々の実務にこのコードを組み込み、圧倒的な作業効率化を体感してくださいね!

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