PowershellでOutlook365のメール定期送信

ありそうで無かったので作成しました。

#ハマりポイント
１．ラジオボタンや凝った装飾のUIはWindows FormじゃなくてWPFの方がいい
　（ラジオボタンが複数ある場合に独立させるのがFormだと面倒）

２．Register-ScheduledTaskは月次未対応なので、月次は旧来のschtasksが必要

３．schtasksのTRの複数引数指定方法はコツが要る

#基本のメール送信モジュール

```powershell:outlookSendMail.ps1
param(
	[string]$ToStr = "",
	[string]$CcStr = "",
	[string]$BccStr = "",
	[string]$TitleStr = "",
	[string]$BodyStr = "",
	[switch]$ShowOnly
)

$processes = [System.Diagnostics.Process]::GetProcessesByName("OUTLOOK")

$collCount = $processes.Length

if ($collCount -ne 0){
	$OutlookObj = [System.Runtime.InteropServices.Marshal]::GetActiveObject("Outlook.Application")
} else {
	$OutlookObj = New-Object -ComObject Outlook.Application
}

$mailaddress = ($OutlookObj.Session.Stores | where{$_.FilePath -like '*.ost'}).DisplayName

$client = New-Object System.Net.WebClient

$sigfile = $env:appdata + "\Microsoft\Signatures\署名１.htm"
#$signature = get-content $sigfile
$content = $client.DownloadString($sigfile)
$parsedHtml = New-Object -com "HTMLFILE"
$parsedHtml.IHTMLDocument2_write($content)
$parsedHtml.Close()
$sigbody = $parsedHtml.getElementsByTagName("body")| select -First 1
$signature = $sigbody.innerText

$Subject = $TitleStr
$Body = $BodyStr + "`n`n" + $signature

$Mail = $OutlookObj.CreateItem(0)
$Mail.To = $ToStr
$Mail.CC = $CcStr
$Mail.BCC = $BccStr
$Mail.Subject = $Subject
$Mail.Body = $Body
$inspector = $Mail.GetInspector

if ($ShowOnly) {
	$inspector.Display()
} else {
	$Mail.Send()
}
```

#定期送信の登録削除モジュール
```powershell:registerRegularMail.ps1
#アセンブリのロード
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

#presentationframeworkを読み込み
Add-Type -AssemblyName presentationframework

[xml]$xaml = @'
<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Outlookメール定期送信WPF"
        mc:Ignorable="d"
        Title="メール定期送信" Height="650.057" Width="720">
    <Grid>
        <Label x:Name="lblTo" Content="To：" HorizontalAlignment="Left" Height="25.004" Margin="18.686,10,0,0" VerticalAlignment="Top" Width="41.881"/>
        <Label x:Name="lblCc" Content="Cc：" HorizontalAlignment="Left" Height="25.004" Margin="18.686,45.004,0,0" VerticalAlignment="Top" Width="46.881"/>
        <Label x:Name="lblBcc" Content="Bcc：" HorizontalAlignment="Left" Height="25.004" Margin="18.686,80.008,0,0" VerticalAlignment="Top" Width="46.881"/>
        <Label x:Name="lblTitle" Content="件名" HorizontalAlignment="Left" Height="25.004" Margin="18.686,116.888,0,0" VerticalAlignment="Top" Width="46.881"/>
        <Label x:Name="lblBody" Content="本文" HorizontalAlignment="Left" Height="25.004" Margin="18.686,175.625,0,0" VerticalAlignment="Top" Width="46.881"/>
        <TextBox x:Name="txtTo" HorizontalAlignment="Left" Height="23" Margin="65.567,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="214.748"/>
        <TextBox x:Name="txtCc" HorizontalAlignment="Left" Height="23" Margin="65.567,45.004,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="214.748"/>
        <TextBox x:Name="txtBcc" HorizontalAlignment="Left" Height="23" Margin="65.567,81.884,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="214.748"/>
        <TextBox x:Name="txtTitle" HorizontalAlignment="Left" Height="23" Margin="18.686,141.892,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="261.629"/>
        <TextBox x:Name="txtBody" HorizontalAlignment="Left" Height="380" Margin="18.686,205.629,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="665.41" AcceptsReturn="True"/>
        <Label x:Name="lblStart" Content="送信開始日時：" HorizontalAlignment="Left" Height="25.004" Margin="290.899,10,0,0" VerticalAlignment="Top" Width="90.274"/>
        <DatePicker x:Name="dtpStart" HorizontalAlignment="Left" Height="25.004" Margin="386.173,7.996,0,0" VerticalAlignment="Top" Width="96.787"/>
        <Label x:Name="lblRegistName" Content="登録名：" HorizontalAlignment="Left" Height="25.004" Margin="290.899,45.004,0,0" VerticalAlignment="Top" Width="90.274"/>
        <TextBox x:Name="txtRegistName" HorizontalAlignment="Left" Height="23" Margin="386.173,45.004,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="191.605"/>
        <GroupBox x:Name="grpShuki" Header="　　周期" HorizontalAlignment="Left" Height="117.938" Margin="605.458,10,0,0" VerticalAlignment="Top" Width="78.638" RenderTransformOrigin="0.365,0.522">
            <StackPanel x:Name="stpShuki" HorizontalAlignment="Left" Height="81.904" Margin="0,10,-2,0" VerticalAlignment="Top" Width="68.638">
                <RadioButton x:Name="rdoGetsuji" Content="月次" Height="18.148" Margin="10,6" Padding="4,-1,0,0" IsChecked="True"/>
                <RadioButton x:Name="rdoShuji" Content="週次" Height="16.635" Margin="10,6,10,8" RenderTransformOrigin="0.479,-0.38"/>
                <RadioButton x:Name="rdoNichiji" Content="日次" Height="15.123" Margin="10,0" RenderTransformOrigin="0.417,2.171"/>
            </StackPanel>
        </GroupBox>
        <GroupBox x:Name="grpExecute" Header="実行内容" HorizontalAlignment="Left" Height="50" Margin="339.289,80.008,0,0" VerticalAlignment="Top" Width="223.825">
            <StackPanel x:Name="stpExecute" HorizontalAlignment="Left" Height="33.349" Margin="10,-6.96,0,0" VerticalAlignment="Top" Width="191.724">
                <RadioButton x:Name="rdoToroku" Content="登録" Height="15.123" Margin="3,12,100,10" Width="45" IsChecked="True"/>
                <RadioButton x:Name="rdoSakujo" Content="削除" Height="13.61" Margin="110,-35,35.709,0" Width="45"/>
            </StackPanel>
        </GroupBox>
        <GroupBox x:Name="grpSoushin" Header="表示のみ／自動送信" HorizontalAlignment="Left" Height="51.884" Margin="339.289,135.008,0,0" VerticalAlignment="Top" Width="223.825">
            <StackPanel HorizontalAlignment="Left" Height="30" Margin="7.611,-5.076,0,0" VerticalAlignment="Top" Width="200.214">
                <RadioButton x:Name="rdoHyoji" Content="表示のみ" Height="15.123" Margin="25.709,10,104.94,10" IsChecked="True"/>
                <RadioButton x:Name="rdoJidou" Content="自動送信" Height="13.61" Margin="113.421,-35,20,0" Width="66"/>
            </StackPanel>
        </GroupBox>
        <Button x:Name="btnExecute" Content="実行" HorizontalAlignment="Left" Height="40" Margin="581.725,146.892,0,0" VerticalAlignment="Top" Width="102.371"/>
        <TextBox x:Name="txtHour" HorizontalAlignment="Left" Height="25.004" Margin="490.984,7.996,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="21.169" Padding="0,3"/>
        <TextBox x:Name="txtSecond" HorizontalAlignment="Left" Height="25.004" Margin="523.204,7.996,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="21.169" Padding="0,3"/>
        <Label x:Name="lblKoron1" Content="：" HorizontalAlignment="Left" Height="25.004" Margin="506.105,7.996,0,0" VerticalAlignment="Top" Width="18.611"/>
        <TextBox x:Name="txtMinute" HorizontalAlignment="Left" Height="25.004" Margin="556.609,7.996,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="21.169" Padding="0,3"/>
        <Label x:Name="lblKoron1_Copy" Content="：" HorizontalAlignment="Left" Height="25.004" Margin="539.51,7.512,0,0" VerticalAlignment="Top" Width="18.611"/>

    </Grid>
</Window>
'@

#Powershellで読み込む時に不要な要素を削除
$xaml.window.RemoveAttribute("x:Class")
$xaml.window.RemoveAttribute("mc:Ignorable")

#読み込んだXAMLをXmlNodeReaderにキャスト
$reader = $xaml -as "System.Xml.XmlNodeReader"

#Windows.Markup.XamlReaderで読み込み
$window = [Windows.Markup.XamlReader]::Load($reader)

#コントロールを読み込み
$lblTo = $window.FindName("lblTo")
$lblCc = $window.FindName("lblCc")
$lblBcc = $window.FindName("lblBcc")
$lblTitle = $window.FindName("lblTitle")
$lblBody = $window.FindName("lblBody")
$txtTo = $window.FindName("txtTo")
$txtCc = $window.FindName("txtCc")
$txtBcc = $window.FindName("txtBcc")
$txtTitle = $window.FindName("txtTitle")
$txtBody = $window.FindName("txtBody")
$lblStart = $window.FindName("lblStart")
$dtpStart = $window.FindName("dtpStart")
$lblRegistName = $window.FindName("lblRegistName")
$txtRegistName = $window.FindName("txtRegistName")
$grpShuki = $window.FindName("grpShuki")
$stpShuki = $window.FindName("stpShuki")
$rdoGetsuji = $window.FindName("rdoGetsuji")
$rdoShuji = $window.FindName("rdoShuji")
$rdoNichiji = $window.FindName("rdoNichiji")
$grpExecute = $window.FindName("grpExecute")
$stpExecute = $window.FindName("stpExecute")
$rdoToroku = $window.FindName("rdoToroku")
$rdoSakujo = $window.FindName("rdoSakujo")
$grpSoushin = $window.FindName("grpSoushin")
$stpSoushin = $window.FindName("stpSoushin")
$rdoHyoji = $window.FindName("rdoHyoji")
$rdoJidou = $window.FindName("rdoJidou")
$btnExecute = $window.FindName("btnExecute")
$txtHour = $window.FindName("txtHour")
$txtSecond = $window.FindName("txtSecond")
$txtMinute = $window.FindName("txtMinute")
$lblKoron1 = $window.FindName("lblKoron1")
$lblKoron2 = $window.FindName("lblKoron2")


#実行ボタンの動作
$btn_clicked = $btnExecute.add_Click
$btn_clicked.Invoke({

	if ($rdoToroku.IsChecked) {

		if ([string]::isNullOrEmpty($txtTitle.Text)) {
			[string]$msg = "件名を入れて下さい。"
			[System.Windows.Forms.MessageBox]::Show($msg, "件名未入力","OK","Error")
			return
		}

		if ([string]::isNullOrEmpty($txtBody.Text)) {
			[string]$msg = "本文を入れて下さい。"
			[System.Windows.Forms.MessageBox]::Show($msg, "本文未入力","OK","Error")
			return
		}

		if ([string]::isNullOrEmpty($dtpStart.Text)) {
			[string]$msg = "日付を選択して下さい。"
			[System.Windows.Forms.MessageBox]::Show($msg, "日付未選択","OK","Error")
			return
		}

		if ([string]::isNullOrEmpty($txtHour.Text)) {
			[string]$msg = "時間（時）を入れて下さい。"
			[System.Windows.Forms.MessageBox]::Show($msg, "時間（時）未入力","OK","Error")
			return
		} else {
			[int]$intHour = [int]$txtHour.Text
			if ($intHour -lt 0) {
				[string]$msg = "時間（時）は0以上で入れて下さい。"
				[System.Windows.Forms.MessageBox]::Show($msg, "時間（時）過小入力","OK","Error")
				return
			} elseif ($intHour -gt 23) {
				[string]$msg = "時間（時）は23以下で入れて下さい。"
				[System.Windows.Forms.MessageBox]::Show($msg, "時間（時）過大入力","OK","Error")
				return
			}
		}

		if ([string]::isNullOrEmpty($txtSecond.Text)) {
			[string]$msg = "時間（分）を入れて下さい。"
			[System.Windows.Forms.MessageBox]::Show($msg, "時間（分）未入力","OK","Error")
			return
		}　else {
			[int]$intSecond = [int]$txtSecond.Text
			if ($intSecond -lt 0) {
				[string]$msg = "時間（分）は0以上で入れて下さい。"
				[System.Windows.Forms.MessageBox]::Show($msg, "時間（分）過小入力","OK","Error")
				return
			} elseif ($intSecond -gt 59) {
				[string]$msg = "時間（分）は59以下で入れて下さい。"
				[System.Windows.Forms.MessageBox]::Show($msg, "時間（分）過大入力","OK","Error")
				return
			}
		}

		if ([string]::isNullOrEmpty($txtMinute.Text)) {
			[string]$msg = "時間（秒）を入れて下さい。"
			[System.Windows.Forms.MessageBox]::Show($msg, "時間（秒）未入力","OK","Error")
			return
		} else {
			[int]$intMinute = [int]$txtMinute.Text
			if ($intMinute -lt 0) {
				[string]$msg = "時間（秒）は0以上で入れて下さい。"
				[System.Windows.Forms.MessageBox]::Show($msg, "時間（秒）過小入力","OK","Error")
				return
			} elseif ($intMinute -gt 59) {
				[string]$msg = "時間（秒）は59以下で入れて下さい。"
				[System.Windows.Forms.MessageBox]::Show($msg, "時間（秒）過大入力","OK","Error")
				return
			}
		}
	}
	
	if ([string]::isNullOrEmpty($txtRegistName.Text)) {
		[string]$msg = "登録名を入れて下さい。"
		[System.Windows.Forms.MessageBox]::Show($msg, "登録名未入力","OK","Error")
		return
	}
	
	if($rdoGetsuji.IsChecked) {
		$runSpan = $rdoGetsuji.Content
	} elseif($rdoShuji.IsChecked) {
		$runSpan = $rdoShuji.Content
	} elseif($rdoNichiji.IsChecked) {
		$runSpan = $rdoNichiji.Content
	}
	
	[string]$time = $txtHour.Text + ":" + $txtSecond.Text + ":" + $txtMinute.Text
	[string]$startDate = $dtpStart.Text
	[string]$startDate2 = (get-date($startDate)).Day
	
	$taskname = $txtRegistName.Text
	
	[string]$ToStr = $txtTo.Text
	[string]$CcStr = $txtCc.Text
	[string]$BccStr = $txtBcc.Text
	[string]$TitleStr = $txtTitle.Text
	[string]$BodyStr = $txtBody.Text
	
	[string]$outlookSendMail = Join-Path $PSscriptRoot "outlookSendMail.ps1"
	[string]$powershellPath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
	
	if($rdoHyoji.IsChecked) {
		$action = New-ScheduledTaskAction -Execute $powershellPath　-Argument "-file $outlookSendMail `"$ToStr`" `"$CcStr`" `"$BccStr`" `"$TitleStr`" `"$BodyStr`" -ShowOnly"
		$filePath = "\`"" + $powershellPath + "\`"" + "-file $outlookSendMail \`"$ToStr\`" \`"$CcStr\`" \`"$BccStr\`" \`"$TitleStr\`" \`"$BodyStr\`" -ShowOnly"
	} elseif($rdoJidou.IsChecked) {
		$action = New-ScheduledTaskAction -Execute $powershellPath　-Argument "-file $outlookSendMail `"$ToStr`" `"$CcStr`" `"$BccStr`" `"$TitleStr`" `"$BodyStr`""
		$filePath = "\`"" + $powershellPath + "\`"" + "-file $outlookSendMail \`"$ToStr\`" \`"$CcStr\`" \`"$BccStr\`" \`"$TitleStr\`" \`"$BodyStr\`""
	}
	
	schtasks /DELETE /TN "$taskname" /F　2>${NULL}
	
	if ($rdoToroku.IsChecked) {
		
		if ($runSpan.Equals("日次"))
		{   
			$trigger = New-ScheduledTaskTrigger -Daily -At $time
			
			Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $taskname
		}

		if ($runSpan.Equals("週次"))
		{
			$week = (get-date($startDate)).DayOfWeek
			
			$trigger = New-ScheduledTaskTrigger -Weekly -At $time -DaysOfWeek $week
			
			Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $taskname
		}        

		#  script out SchTasks magic to create the monthly tasks
		if ($runSpan.Equals("月次"))
		{
			$taskParams = @("/Create",
						"/TN", $taskname, 
						"/SC", "monthly", 
						"/D", $startDate2, 
						"/ST", $time, 
						"/TR", "$filePath", 
						"/F");

			# supply the command arguments and execute  
			schtasks.exe @taskParams
		}
	}
	
	[string]$msg = "完了しました。"
	[System.Windows.Forms.MessageBox]::Show($msg, "完了", "OK", "Info")

	taskschd.msc
})

# ウィンドウ表示
$window.ShowDialog() > $null
```