PowerShellでOutlookのメールを作成する

丸々利用できるコードを見つけられなかったので、調べて、継ぎ接ぎして、何とか動くものにしました。
下書き保存するだけのコードにしてはいますが、メールに関わるものですので、利用・テストされる際は誤送信など十分ご注意ください。

##お礼
コード中、COMオブジェクトの破棄する箇所は@nukie_53 さんに教わったものを使わせていただきました。:sweat_smile:
ありがとうございます。
この関数は今後もCOMがらみでは必携となりそうです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">最近のツイートの感じからCOMの解放関係かな？ということで参考程度に。<br>[ref]クラスにすると、関数などでも参照渡し風にできるのでそれを使っています。<br><br>複数変数まとめては難しいのでこれでも面倒ですけれど…<br>処理によってはStart-Jobとかpowershell新規起動でプロセス終了の自動解放に任せる手も <a href="https://t.co/LMNiKRjcaa">pic.twitter.com/LMNiKRjcaa</a></p>&mdash; いみひと (@nukie_53) <a href="https://twitter.com/nukie_53/status/1197796351557263361?ref_src=twsrc%5Etfw">November 22, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

##コード
outlookが起動済みか否かによらず、動きます。
一応、outlookで他で編集中のものがない方がよいです。（あれば、保存するかどうかのメッセージが出て、outlookの終了前で止まるようですが。）


```powershell:メール作成して下書き保存
# すでにプロセスが存在するか調べる。
$TEST =Get-Process|Where-Object {$_.Name -match "OUTLOOK"}
if ($TEST -eq $null){
    $existsOutlook = $false
}else{
    $existsOutlook = $true
    }

# プロセスを起動または取得。PowerShellを管理者実行してると，普通に開いたオブジェクトを取ることが出来ない．
if ($existsOutlook) {
    $OutlookObj = [System.Runtime.InteropServices.Marshal]::GetActiveObject("Outlook.Application")
} else {
    $OutlookObj = New-Object -ComObject Outlook.Application
    }

#新規メールの作成。定数OlItemTypeの中身を渡す。0がメールで1が予定。あとは知らない･･･。
$mail=$OutlookObj.CreateItem(0)
#内容入力
$mail.Subject ="お暇なら来てよね"
$mail.Body ="来たら話します"
#表示は連絡帳によらず「Aさん」、「Bさん」となるはず。
$mail.To ="Aさん<aimaimoko@xxx.com>"+"; "+"Bさん<bottakuri@yyy.com>"

#添付するファイルの検索、添付。繰り返せば、添付ファイルが増える仕様。
$Attach_A =Get-ChildItem C:\Users\Public\Documents | ? {$_.Name -match "value.txt$"}
$mail.Attachments.Add($Attach_A.FullName)|Out-Null
$mail.Close(0)

#***********いみひとさん（@nukie_53）より*********************************
function Disconnect-ComObject{
    param(
        [Parameter(ValueFromPipeLine =$true)]
        [ref]$inputObject
    )    
    if ($inputObject.Value -is [System.__ComObject]){
        
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($inputObject.Value)|Out-Null
        $inputobject = $null
        [System.GC]::Collect()
        [System.GC]::WaitForPendingFinalizers()
        [System.GC]::Collect()
    }
}
#*********************************************************

#以下、COMオブジェクトの破棄を実行。ref型にして渡す。
[ref]$mail | Disconnect-ComObject
#当初プロセスがない場合、メールを閉じたらもう何も開いていない状態になるためか、quitメソッドでエラーが出る。それを回避。
if ($existsOutlook) {$OutlookObj.Quit()}
[ref]$OutlookObj |Disconnect-ComObject

```
##参考サイト
###outlook

- [MailItem object (Outlook)](https://docs.microsoft.com/ja-jp/office/vba/api/outlook.mailitem)
- [Application.Quit method (Outlook)](https://docs.microsoft.com/ja-jp/office/vba/api/outlook.application.quit(method))

###.NET

- [Marshal.GetActiveObject(String) メソッド](https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.marshal.getactiveobject?view=netframework-4.8) 


###PowerShell
- [静的なクラスとメソッドの使用｜MicroSoft Docs](https://docs.microsoft.com/ja-jp/powershell/scripting/samples/using-static-classes-and-methods?view=powershell-6)
- [演算子について｜Microsoft Docs](https://docs.microsoft.com/ja-jp/PowerShell/module/microsoft.powershell.core/about/about_operators?view=powershell-6)
- [.NETを使ったOfficeの自動化が面倒なはずがない―そう考えていた時期が俺にもありました。｜@mima_ita さん](https://qiita.com/mima_ita/items/aa811423d8c4410eca71)


##感想など
###ref型
これを使えば、スコープを超えて、参照元を操作できるのですね。下記のような具合。

```powershell:ref型の実験
$a =123
function testref{
    param([ref]$InputA)
    $InputA.Value =456
}
testref([ref]$a)
$a
###456が返る
```
###連絡帳から引っ張ってくる方法
これが分からなかったのが、心残り。
VBAでは`MailItem.Recepients.Add`なる記法が通るのですが、PowerShellではインテリセンスが効かなかったので、諦めました。
###宛先を複数にする場合の記法
実験して上手く行っただけなので、もっといい記法はあるかもしれません。
###発展性
Excelファイル等から宛先と呼称を二次配列に入れて、それを宛先の文字列として組み立てるというところまでやるのが本来なんでしょうね。
当座はそこまで要らなかったので、ベタ打ちにしています。

##テストした環境
Windows7
Office365のOutlook（バージョン1911）
PowerShell　5.1