背景
COM オブジェクトを利用して Office 操作しようとすると変数のガベージコレクトが大変です。何より背後で一度アプリケーション自体が立ち上がっているので遅くてやっていられません。
そこで、 現在開いているファイルを powershell から操作する ようにしてみたらとても便利だったので情報共有です。
アクティブな Office を捕まえる
事前準備として、下記コードを $profile
に書いておくかドットソースで読み込んでおきます。
function Get-ActiveOffice {
<#
.SYNOPSIS
現在アクティブなOfficeアプリケーションを捕まえる
.PARAMETER app
Office ソフトウェア
#>
param (
[ValidateSet("Word.Application","Excel.Application", "PowerPoint.Application")][string]$app
)
try {
$office = [System.Runtime.InteropServices.Marshal]::GetActiveObject($app)
if ($app -eq "Word.Application") {
if ($office.Documents.Count -lt 1) {
return $null
}
}
elseif ($app -eq "Excel.Application") {
if ($office.Sheets.Count -lt 1) {
return $null
}
}
elseif ($app -eq "PowerPoint.Application") {
if ($office.Presentations.Count -lt 1) {
return $null
}
}
return $office
}
catch {
return $null
}
}
Excel を開いていてもすべてのシートが閉じられているなどの状態では何も返さないように、適宜ソフトの種類に応じて条件分岐させています。
そして以下のようにして変数に格納してしまえば、あとは開いているファイルを powershell から操作できるようになります。 文法的には VBA と同一です。 また、 ガベージコレクトが不要です。
# Excel を操作してみる
PS > $e = Get-ActiveOffice "Excel.Application"
PS > $e.Selection.Cells.Value = "hogehoge" # 選択しているセルに「hogehoge」と入力される
powershell が文字通りコンソール(操作盤)となるわけです。
なお、VBA と同様に Excel を操作した場合は Ctrl+Z
で元に戻すことができませんが、 Word と PowerPoint ならやり直しが効きます。
# Word を操作してみる
PS > $w = Get-ActiveOffice "Word.Application"
PS > $w.Selection.Font.Bold = $true # 選択している文字が太字になる(Ctrl+Zでアンドゥ可能!)
# PowerPoint を操作してみる
PS > $p = Get-ActiveOffice "PowerPoint.Application"
PS > $p.ActivePresentation.Slides.Add(1, 12) # 空白のスライドが追加される
そのままファイルを保存して閉じればプロセスも残りません。マクロの知識を組み合わせれば無限の可能性が開けてきます。また、よく使うマクロを PERSONAL.xlsb
に保存しておく代わりに .ps1
に書いておいて powershell から実行できるようになるため、 git などによるバージョン管理なども容易になります。
もっと操作しやすくする
上記コマンドレットをラップして、直接 ActiveDocument
や ActiveSheet
を取得できるようにしました。VBA でのおなじみの書き方に近づきます。
function Get-ActiveDocument {
<#
.SYNOPSIS
現在アクティブなWord文書を捕まえるコマンドレット
#>
$word = Get-ActiveOffice "Word.Application"
if (-not $word) {
return $null
}
return $word.ActiveDocument
}
function Get-ActiveSheet {
<#
.SYNOPSIS
現在アクティブなExcelのシートを捕まえるコマンドレット
#>
$excel = Get-ActiveOffice "Excel.Application"
if (-not $excel) {
return $null
}
return $excel.ActiveWorkbook.ActiveSheet
}
PowerPoint は力尽きました……
組み込み定数
VBA の組み込み定数は使用できません。各定数の値を数値として指定する必要があります。毎度ググって入力するのも手間なので記事にまとめました。