解决方案: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) }