1

問題:

電子メールを送信する PowerShell スクリプトは、対話型セッションから呼び出されると機能しますが、スケジュールされたタスクを介して呼び出されると失敗します。これは、スクリプトの終了時に SMTP セッションが送信中に中断されることが原因のようです。スクリプトが終了した後もセッションは存続するため、対話型セッション中には当然、これは発生しません。ただし、スケジュールされたタスクを介して呼び出された場合、セッションはスクリプトの最後で強制終了されます。

脚本:

    $msg = New-Object Net.Mail.MailMessage
    foreach( $To in $Recipients ) {
      $msg.To.Add($To)
    }
    $msg.From = $From
    $msg.Subject = $Subject
    $msg.Body = $Body
    $ctype = New-Object Net.Mime.ContentType -Property @{
      MediaType = [Net.Mime.MediaTypeNames+Application]::Octet
      Name = $AttachmentName
    }
    $msg.Attachments.Add([Net.Mail.Attachment]::CreateAttachmentFromString($csv, $ctype))
    $client = new-object System.Net.Mail.SmtpClient($SMTPServer)
    $client.Send($msg)

質問

System.Net.Mail.SmtpClient::Send() は同期的であると想定されていますが、実際にはそのようには機能していません。スクリプトの最後にスクリプトを短時間スリープさせることで、これをいくらか軽減できることはわかっています。率直に言って、それは非常にずさんな作業であり、私はそのようにすることを拒否します. 最善の回避策は何ですか?

注:Send-MailMessage添付ファイルを作成するためにファイルを作成することは避けたいので、適切ではありません。同じ問題に苦しんでいるかどうかはわかりません。

4

1 に答える 1

2

解決策: SendAsync()

Send()ドキュメントが主張するほど同期していないのは残念です。SendAsync()代わりに非同期バリアントを使用し、SendCompletedイベントを使用して同期性を提供することが最も有益であることがわかりました。この問題を解決するために私が書いた次の汎用関数を参照してください (ただし、さまざまな非同期呼び出しに使用できます)。

<#
.SYNOPSIS
  Calls a script block and waits for the specified event.
.DESCRIPTION
  Provides a convenient way to call an asynchronous method with synchronous semantics.
  This is achieved by registering to receive an event before calling the block that is
  expected to signal its completion via a .NET event.  The given script block need not
  necessarily cause the event to be signaled, but that is the most obvious use case.
.PARAMETER EventSource
  The object expected to raise the event.
.PARAMETER EventName
  The name of the expected event.
.PARAMETER ScriptBlock
  This block will be executed after registering the event and before waiting for the event.
.PARAMETER Timeout
  Maximum duration in seconds to wait for the expected event.  Default value of -1 means
  to wait indefinitely.  No error is raised when this timeout is reached.
.NOTES
  Author:  Erik Elmore <erik@ironsavior.net>
#>
function Wait-AsyncCall {
  Param(
    [Parameter(Mandatory = $True, Position = 0)]
    $EventSource,
    [Parameter(Mandatory = $True, Position = 1)]
    [string]$EventName,
    [Parameter(Mandatory = $True, Position = 2)]
    [ScriptBlock]$ScriptBlock,
    [int]$Timeout = -1
  )
  $id = "Wait-AsyncCall:$($EventName):$([guid]::NewGuid().ToString())"
  Register-ObjectEvent $EventSource $EventName -SourceIdentifier $id -EA Stop
  try {
    $output = &$ScriptBlock
    Wait-Event -SourceIdentifier $id -Timeout $Timeout -EA SilentlyContinue |Remove-Event -EA SilentlyContinue
  }
  finally {
    Unregister-Event -SourceIdentifier $id -EA SilentlyContinue
  }
  $output
}

を使用Wait-AsyncCallすると、代わりにAsyncSend()メソッドを使用できますが、同期セマンティクスを使用します。元のコード ブロックは次のようになります。

$msg = New-Object Net.Mail.MailMessage
foreach( $To in $Recipients ) {
  $msg.To.Add($To)
}
$msg.From = $From
$msg.Subject = $Subject
$msg.Body = $Body
$ctype = New-Object Net.Mime.ContentType -Property @{
  MediaType = [Net.Mime.MediaTypeNames+Application]::Octet
  Name = $AttachmentName
}
$msg.Attachments.Add([Net.Mail.Attachment]::CreateAttachmentFromString($csv, $ctype))
$client = new-object System.Net.Mail.SmtpClient($SMTPServer)
Wait-AsyncCall $client "SendCompleted" { $client.SendAsync($msg, $Null) }
于 2013-03-21T21:44:08.267 に答える