PowerShellでサブフォルダを含む複数エクセルファイルを一括PDF変換する

## 概要
Windowsで、選択したExcelファイル又は選択したフォルダ内のサブフォルダ内を含む全てのExcelファイルを、一括でPDFに変換する、PowerShellスクリプト埋め込みBatファイルです。
とある業務でテスト仕様書結果書など100ファイルを超えるExcel文書を印刷体裁含めて内容のレビューを行う必要があり、Excelのまま見るよりPDF変換してから見る方がレビュー効率がいいため、そのPDF変換作業が楽になるように作りました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257858/d8a46d45-dbf8-59db-93cf-d2c4194c81a6.jpeg" width=50%>
↑　こういうエクセルファイル類を全部PDF変換する時の手間を無くしたい

## 特徴
* 右クリックコンテクストメニューからのシンプルな操作
* 複数フォルダ同時選択やフォルダとファイルの複数同時選択も対応

## コード
```powershell:Excel2PDF.bat
<# : by earthdiver1
@echo off & setlocal EnableDelayedExpansion
set BATCH_ARGS=%*
for %%A in (!BATCH_ARGS!) do set "ARG=%%~A" & set "ARG=!ARG:'=''!" & set "PWSH_ARGS=!PWSH_ARGS! "'!ARG!'""
if defined PWSH_ARGS set "PWSH_ARGS=!PWSH_ARGS:^^=^!"
endlocal &  Powershell -NoProfile -Command "$input|&([ScriptBlock]::Create((gc '%~f0'|Out-String)))" %PWSH_ARGS%
exit/b
: #>
Function Main($aryArgs) {
    try {
        # 誤操作防止の確認
        Write-Host "全てのExcelファイルについてPDFファイルを作成します" -Fore Yellow
        Write-Host "続行する場合は 1 を選択してください  0:いいえ、1:はい [0]：" -NoNewline -Fore Yellow
        try { $Answer = Read-Host } catch [System.Exception] { $Answer = -1 }
        switch -exact ($Answer) {
            0       { Write-Host "処理を終了します。" -Fore Green ; exit }
            1       { } #処理続行
            default { Write-Host "不正な値が入力されました。処理を終了します。" -Fore Red ; exit }
        }
        # Excelインスタンスを非表示で作成
        $excel = New-Object -ComObject Excel.Application
        $excel.Visible = $false
        $excel.DisplayAlerts = $false
        # 引数毎に処理
        ForEach ($arg in $aryArgs) {
            if ((Get-Item $arg).PSIsContainer) { # 今回の引数がフォルダの場合
                # サブフォルダまで含めてExcel拡張子ファイルを取得
                $files = Get-ChildItem $arg -include *.xlsx,*.xlsm,*.xls -Recurse -Force | ForEach-Object {$_.FullName}
                ForEach ($file in $files) {
                    # 処理用にオブジェクト化
                    $fileitem = Get-Item $file
                    Write-Host "■処理中：" $fileitem.FullName # バッチプロンプトに進捗状況を出力
                    # ファイルオープン
                    $book = $excel.Workbooks.Open($fileitem.FullName, $false, $true)
                    # PDF出力先を、Excelファイルと同じ場所で拡張子以外を同じ名前に設定
                    $pdfpath = $fileitem.DirectoryName + "\" + $fileitem.BaseName + ".pdf"
                    # PDF出力
                    $book.ExportAsFixedFormat([Microsoft.Office.Interop.Excel.XlFixedFormatType]::xlTypePDF, $pdfpath)
                    # ファイルクローズ
                    $book.Close()
                    [Void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($book)
                }
            } else { # 今回の引数がファイルの場合
                # Excel拡張子ファイルかどうか確認
                if ((Get-Item $arg).Extension -Like ".xls*") {
                    # 処理用にオブジェクト化
                    $fileitem = Get-Item $arg
                    Write-Host "■処理中：" $fileitem.FullName # バッチプロンプトに進捗状況を出力
                    # ファイルオープン
                    $book = $excel.Workbooks.Open($fileitem.FullName, $false, $true)
                    # PDF出力先を、Excelファイルと同じ場所で拡張子以外を同じ名前に設定
                    $pdfpath = $fileitem.DirectoryName + "\" + $fileitem.BaseName + ".pdf"
                    # PDF出力
                    $book.ExportAsFixedFormat([Microsoft.Office.Interop.Excel.XlFixedFormatType]::xlTypePDF, $pdfpath)
                    # ファイルクローズ
                    $book.Close()
                    [Void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($book)
                }
            }
        }
        # Excelインスタンス終了
        $excel.Quit()
        [Void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
        [GC]::Collect()

    } catch [System.Exception] {
        Write-Host "システム例外が発生しました。" -Fore Red
        Write-Host $Error[0].Exception
        Read-Host "何かキーを押すと終了します。" 
    } finally {
    }
}

Main $Args
```

## 使い方
1. このコードをbatファイル形式で保存して、batファイルへのショートカット又はbatファイル本体を、以下に格納します。  
C:\Users\username\AppData\Roaming\Microsoft\Windows\SendTo  
 ※[Windowsキー]+[R]で「ファイル名を指定して実行」から[shell:sendto]で開く場所です。
2. PDF変換したいエクセルファイル、又はエクセルファイルを含むフォルダを右クリックして「送る」メニューからbatファイル(又はショートカット)を選べば、エクセルファイルと同じ場所に、同名のPDFファイルが作成されます。<Br>複数のファイル・フォルダの同時選択、フォルダ内のサブフォルダに含まれるエクセルファイルに対しても、処理できます。

## コードの流れ
* BatファイルにPowerShellスクリプトを埋め込んでおり単一ファイルでスクリプト実行可能です。
* 右クリック「送る」へ送ったファイルパスやフォルダパスは、batファイルへの引数として受け取り、さらにpowershellスクリプトの引数として渡されます。
* powershellスクリプトでExcel.ApplicationのComObjectを生成し、引数で受け取ったファイルパスやフォルダパスからエクセルファイルを取得して非表示・読み取り専用でファイルを開き、PDF変換を行って、エクセルファイルを閉じます。
* 引数毎にループ処理しており、フォルダの場合はサブフォルダまでエクセルファイルを一斉に取得するので、複数フォルダ選択時にもサブフォルダが存在する場合にも、対応しています。

## 課題
* PDFファイルの出力先をExcelファイルと同じ場所にしていますが、PDFファイルの削除や移動などを含め該当フォルダを触りたくない、読み取り権限はあるけど書き込み権限は無い、などの場合を想定すると、PDFファイルの出力先ルートフォルダを別に指定できるようにもしておきたいです。  
 **<font color="Blue">⇒ 2020.09.22 PDFファイルの出力先ルートフォルダを別に指定できるよう対応（コメントへ投稿）</font>**
* 拡張子の指定方法がフォルダとファイルで異なるので出来れば統一したいです。
* WordやPowerPointもComObjectのメソッドが少し変わるだけで同じ事が出来そうなので、もし必要になったらWord版やPowerPoint版も、出来れば同一スクリプト内でスイッチして処理できればいいなと思います。


## 参考・引用
以下の記事をベースに、BatファイルにPowerShellスクリプトを埋め込む制御や引数の受け取り方を流用しました。
[レジストリ修正不要で、エクセルファイルを右クリックメニューから読み取り専用で開く](https://qiita.com/lifequery/items/949a040fa431c4d05d61)
クリエイティブ・コモンズ 表示 - 継承 4.0 国際