最近人事/総務からのヒアリングした業務上の問題を解決していく、という社内プロジェクトに参加しています。

その中で、割と調べても出てこなかった事柄をメモしておきます。

要望

今回解決したいのは、次のような問題でした。

とにかく問題としては、 Excelを開く→最初のページだけ印刷する という業務が多く、かつ時間がかかる、と。電子承認とかそういう方向に行きたいらしいですが、まーいろいろ事情があるようで。

方針の検討

とりあえず抜本的ではないが、ある程度作る労力と、結果の省力化に貢献できるものとして、次のようなツールを作ることにしました。

困ったこと

人事/総務の方々は、ITレベルが様々なので、Windows標準で入っているものを使う、ということで、 PowerShell を使うことにしました。

ところが、いろいろ調べて(ググって)みても、 ExcelからBookを印刷する というものはあっても、 指定したページだけ印刷する ということをやっている人がまずいないっぽいという・・・。

フォルダ内のExcelを印刷するという回答

色々試す

色々試した所、次のサイトが気づきになりました。

https://docs.microsoft.com/ja-jp/office/vba/api/excel.workbook.printout

このページ、Office VBAのヘルプですが、ここに引数が書いてあります。

名前必須 / オプションデータ型説明
From省略可能Variant印刷を開始するページの番号を指定します。 この引数を省略すると、最初のページから印刷します。
To省略可能Variant印刷を終了するページの番号を指定します。 この引数を省略すると、最後のページまで印刷します。
Copies省略可能Variant印刷部数を指定します。 この引数を省略すると、印刷部数は 1 部になります。
Preview省略可能VariantTrue の場合、印刷をする前に印刷プレビューを実行します。 False、または省略した場合、直ちに印刷を行います。
ActivePrinter省略可能Variantアクティブなプリンターの名前を指定します。
PrintToFile省略可能VariantTrue の場合、ファイルへ出力します。 引数 PrToFileName が省略された場合、出力先のファイル名を指定するためのダイアログ ボックスを表示します。
Collate省略可能VariantTrue の場合、部単位で印刷します。
PrToFileName省略可能Variant_PrintToFile_がTrueに設定されている場合、この引数は印刷先のファイル名を指定します。
IgnorePrintAreas省略可能VariantTrue の場合、印刷範囲を無視してオブジェクト全体を印刷します。

これをPrintOutに指定すればいいんじゃね!?ということで、こんな感じにしてみました。

filter printExcel($StartPage, $EndPage, $FileName) {
    $Excel = New-Object -comobject Excel.Application
    $Excel.Visible = $false   # Excel自体は表示しない
    $book = $Excel.Workbooks.Open($FileName)

    $Missing = [System.Reflection.Missing]::Value
    $OutputFileName = $Missing
    $PrintToFile = $false
    $From        = $StartPage
    $To          = $EndPage
    $Item = 0
    $Collate = $Missing
    $Copies  = 1
    $Preview = $false
    $IgnorePrintAreas = $Missing
    $ActivePrinter = $Missing

    $book.PrintOut.Invoke(@($From, $To, $Copies, $Preview, $ActivePrinter, $PrintToFile, $Collate, $OutputFileName, $IgnorePrintArea))
    $Excel.Quit()
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel) | Out-Null
}

肝は PrintOut.Invoke です。PrintOut自体はMethodという実体なんですが、こいつ自体は複数の引数を設定できないようでした。(実際、渡すと引数の数が違うと言われる)

ですが、デバッグしてみると、 Invoke というメソッドが追加で生えているのを見つけました。そこからは、COMオブジェクトとかそういうものは、大抵配列を引数で受け取る(lispのapplyとかそういう感じ)ものなので、PowerShell上で配列を使ってみたら、無事動作しました。

ちなみに、ActivePrinterをPDFとかにして、OutputFileNameを指定すると、指定したファイルにPDFが吐かれるので、そういう応用も効く感じです。

Windows + Officeを扱うときにPowerShellという選択肢

実際には、このツールはXAMLを利用したGUIも付けた状態にしていますが、実作業時間としては半日くらいで作っています。PowerShell自体は、型の明記が出来たり、連想配列が最初からサポートされていたりと、割と使いやすい印象でした。スコープの概念が若干わかりづらかったですが。

ExcelとかWordとかと戦わないといけないが、長く(多分・・・)使わないツール、とかは、PowerShellで書いていくのもいいんじゃないでしょうか。コードなので問題なくGitとかでも管理できます。

大量にあるExcelから指定のページだけをひたすら印刷しないといけない、みたいなときにこの記事が役に立てば。