PowerShellでエクセルスクショツール PowerPoint版

[Qiita1]:http://qiita.com/asterisk9101/items/a49ebe010e19e5c62f98 "2016年度版エクセルスクショ取得ツール"

[COMRelease]:https://blogs.msdn.microsoft.com/office_client_development_support_blog/2012/02/09/office-5/ "Office オートメーションで割り当てたオブジェクトを解放する – Part1"

#1. はじめに
[こちら][Qiita1]の方のコードをベースにPowerPoint版を作ってみた。
Excelでやらざるを得ないから、Excelで作らないと意味が無いことは重々承知だが、自分の勉強用ということで。

#2. コード
```ps1
param($path)

#UserSetting
[int32]$sleepTime = 100
[String]$exitTxt = 'exit' #この文字列をコピーしても終了する（大文字可）
#/UserSetting

function logger($msg) {
    Write-Host (Get-Date -Format "u")"`t"$msg
}
logger "Processing..."

#COMオブジェクト格納配列
[int32]$arrayUbound = 9
[Object[]]$coms = 0..$arrayUbound
for ([int32]$i=0;$i -le $arrayUbound;$i++){
    $coms[$i] = $null
}

#配列添え字
New-Variable -Name ppApp    -Value 0 -Option ReadOnly -Description 'PowerPoint.Application'
New-Variable -Name ppPress  -Value 1 -Option ReadOnly -Description 'PowerPoint.Presentations'
New-Variable -Name ppPres   -Value 2 -Option ReadOnly -Description 'PowerPoint.Presentation'
New-Variable -Name ppSlds   -Value 3 -Option ReadOnly -Description 'PowerPoint.Slides'
#Loop
New-Variable -Name ppSld    -Value 4 -Option ReadOnly -Description 'PowerPoint.Slide'
New-Variable -Name ppShps   -Value 5 -Option ReadOnly -Description 'PowerPoint.Shpaes'
New-Variable -Name ppPic    -Value 6 -Option ReadOnly -Description 'PowerPoint.Shape Pasted Picture'
New-Variable -Name ppTitle  -Value 7 -Option ReadOnly -Description 'PowerPoint.Shpae Title PlaceHolder'
New-Variable -Name ppTxtFrm -Value 8 -Option ReadOnly -Description 'PowerPoint.TextFrame Title'
New-Variable -Name ppTxtRng -Value 9 -Option ReadOnly -Description 'PowerPoint.TextRange Title'

#PowerPoint定数
New-Variable -Name ppLayoutTitleOnly -Value 11 -Option ReadOnly -Description 'PowerPoint.PpSlideLayout.ppLayoutTitleOnly'

#System.Windows.Forms.Clipboardを使用するため
Add-Type -AssemblyName System.Windows.Forms

try{
    $coms[$ppApp] = New-Object -ComObject PowerPoint.Application
    logger "Connected to PowerPoint."

    $coms[$ppPress] = $coms[$ppApp].Presentations

    if ($path -ne $null) {
        try {
            $coms[$ppPres] = $coms[$ppPress].Open($Path)
            logger "Open $Path"
        } catch {
            logger "File Open Failed. => $Path"
            return;
        }
    } else {
        $coms[$ppPres] = $coms[$ppPress].Add($true)
    }

    $coms[$ppSlds] = $coms[$ppPres].Slides

    logger "Clear Clipboard."
    do {
        if ([System.Windows.Forms.Clipboard]::ContainsText()){
            #$exitTxtをコピーすると終了
            if ([System.Windows.Forms.Clipboard]::GetText() -eq $exitTxt) {
                [System.Windows.Forms.Clipboard]::Clear()
                break
            }
        }
        if ($coms[$ppApp].Visible -eq $False) {
            logger "PowerPoint Closed."
            $coms[$ppApp].Quit()
            break
        }

        if ([System.Windows.Forms.Clipboard]::ContainsImage()) {
            Measure-Command {
                
                #現在のスライド枚数を参照して、末尾に挿入する。動作中にPowerPointを編集しない前提なら、カウント用変数を用意した方が早い。
                $coms[$ppSld]  = $coms[$ppSlds].Add($coms[$ppSlds].Count + 1, $ppLayoutTitleOnly)
                $coms[$ppShps] = $coms[$ppSld].Shapes
                $coms[$ppPic]  = $coms[$ppShps].Paste()
                <# 画像を操作をするならここに記述追加 #>

                $coms[$ppTitle]  = $coms[$ppShps].Title
                $coms[$ppTxtFrm] = $coms[$ppTitle].TextFrame
                $coms[$ppTxtRng] = $coms[$ppTxtFrm].TextRange
                $coms[$ppTxtRng].Text = Get-Date -Format "yyyy/MM/dd HH:mm:ss"

                [System.Windows.Forms.Clipboard]::Clear()
                
            } | % { logger $_.TotalMilliseconds.ToString(("#####.000ms").PadLeft(10)) }
        } else {
            Start-Sleep -Milliseconds $sleepTime
        }
    } while($true)
}catch{
    Out-Host $Error
    throw
}finally{
    <#
    COMオブジェクト解放処理
    参考：Office オートメーションで割り当てたオブジェクトを解放する – Part1
    https://blogs.msdn.microsoft.com/office_client_development_support_blog/2012/02/09/office-5/
    #>
    for ([int32]$i=$arrayUbound;$i -ge 0;$i--){
        if ($coms[$i] -ne $null) {
            [System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($coms[$i]) >$null
            $coms[$i] = $null
        }
    }
    
    #強制解放：他と連携しないなら無くても？
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()
    [System.GC]::Collect()

    logger "Terminating..."
}
```

PowerPointだと画面依存の操作を少なくできるので、割と適当に書いても動くのがポイント。

Excel-PowerPoint間の違い以外に手を加えた点としては、COMオブジェクトの解放処理（最後の"finally"ブロック内）を追加。
Object型の配列(`$coms`)にCOMオブジェクトを入れておくことで参照解放の記述を書く手間を減らしている。
また、配列の添え字として大量の変数(`$pp~`)を宣言しているが、PowerShell v5から列挙体が使えるらしいので、そちらを使えば記述がきれいになる。

#参考
+ [Office オートメーションで割り当てたオブジェクトを解放する – Part1][COMRelease]
+ [2016年度版エクセルスクショ取得ツール][Qiita1]