通过函数调用从 PSObject 进行奇怪的 Object[] 转换

Weird Object[] casting from PSObject through a function call

我目前正在编写一个能够将 ADO(来自电子邮件的 EML 附件)转换为 PSObject 的函数。代码存根如下所示:

function Get-OriginalMailAttributes([__ComObject]$email){
    #.DESCRIPTION Downloads in Temp the attachment and open it.
    # Returns PSObjectwith values.

    #STEP 1: Download EML
    $TEMPFILE = "..."

    if($email.Attachments.Count){
        $attachment=$email.Attachments|?{$_.Filename.endsWith(".eml")} | select -First 1
        $fileName = $TEMPFILE + $(New-Guid | select -exp Guid) + ".eml"
        $attachment.SaveAsFile($fileName)

        #STEP2 : Retrieve EML Objects
        $adoDbStream = New-Object -ComObject ADODB.Stream
        $adoDbStream.Open()
        $adoDbStream.LoadFromFile($fileName)
        $cdoMessage = New-Object -ComObject CDO.Message
        $cdoMessage.DataSource.OpenObject($adoDbStream, "_Stream")

        #STEP 3: Bind values
        $attributes = New-Object PSObject -Property @{
            From = [string]($cdoMessage.Fields.Item("urn:schemas:mailheader:from").Value.toString())
        }

        #STEP 4: Cleanup
        [System.Runtime.InteropServices.Marshal]::ReleaseComObject($cdoMessage)
        [System.Runtime.InteropServices.Marshal]::ReleaseComObject($adoDbStream)
        Remove-Item $fileName

        return $attributes
        # Note that the debugger acknowledge the fact that $attributes is PSObject.
    }
}

然后我会在某个时候调用该函数:

$sender_raw = Get-OriginalMailAttributes $email
$sender_raw.getType()

IsPublic IsSerial Name     BaseType
-------- -------- ----     --------
True     True     Object[] System.Array

$sender_raw
0
0

From
----
"Bob" <dummy@contoso.com>
$sender_raw.From # Crashes.

这种行为从何而来?

通常,当您在函数的输出中有“额外”值时,这是因为您正在调用具有 return 值的方法,而不是处理这些值。

.Add() 方法因输出已添加项目的索引而臭名昭著。

为避免此问题,您有以下几种选择:

  • 转换为无效:[void]$collection.Add($item)
  • 分配给 $null:$null=$collection.Add($item)
  • 管道到 out-null:$collection.Add($item) | out-null

我在您的代码中没有看到任何明显的方法,但是在整个代码中添加 Write-output 'Before Step 1' 等应该可以清楚地说明违规语句的位置。

这种奇怪的行为来自 multi-returns 来自 PowerShell 函数。

PS> function f() {
  1+1
  return 4
}

PS> f
2
4

简而言之,如果 one-liners 是 returning 值,它们将作为 Object[] 数组添加到输出中。 虽然这是处理函数的 return 值的一种特别混乱的方式,但我对我的代码的解决方案是重定向输出 NULL:

    stub...
    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($cdoMessage) | Out-Null
    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($adoDbStream) | Out-Null

Suppressing return values in PowerShell functions