PowerShell Invoke-Command返回COM对象属性读取异常问题
问题产生原因
- PowerShell 远程执行(
Invoke-Command)的返回值会经过WinRM通道做序列化/反序列化处理,仅对托管的、有明确序列化规则的.NET对象有完整的数据还原能力。 - 你通过
Microsoft.Update.Searcher拿到的搜索结果是原生COM对象,不属于可直接序列化的托管类型,跨会话回传时只会返回一个没有实际数据的COM代理壳(也就是你看到的System.__ComObject),本地会话无法通过这个代理跨会话访问远程COM对象的Updates集合和对应属性,自然遍历不到任何内容。 - 第一版代码之所以能正常输出,是因为所有COM对象操作都在远程会话本地完成,没有跨会话传递COM对象的步骤,不存在序列化丢失的问题。顺带提一句,你贴的第一版示例代码有笔误:脚本块内没有把
$searcher.Search()的返回值赋值给$tmp,直接访问$tmp.Updates理论上会报空值错误,应该是写示例时漏了赋值语句。
修复方案
核心原则:所有COM对象的属性读取、方法调用全部放在远程会话侧完成,仅将提取出来的、无COM依赖的普通结构化数据回传到本地,避免跨会话传递非序列化友好的COM对象。
修复后的完整可运行代码如下:
param( $updateId = $false, $hostName = $false ) if(($updateId -eq $false) -or ($hostName -eq $false)) { Write-Host "checkUpdateInstalled.ps1 -updateId <updateIdValue> -hostName <Remote Host Name>" exit } # 远程侧完成所有COM操作,仅回传普通结构化对象 $localUpdateResult = Invoke-Command -ComputerName $hostName -ScriptBlock { $searcher = New-Object -ComObject Microsoft.Update.Searcher $searchResult = $searcher.Search("IsInstalled=1 AND UpdateID='$Using:updateId'") $output = @() # 远程侧遍历COM集合,提取需要的字段转成普通PS自定义对象 foreach($update in $searchResult.Updates){ $output += [PSCustomObject]@{ Title = $update.Title Description = $update.Description UpdateID = $update.Identity.UpdateID RevisionNumber = $update.Identity.RevisionNumber KBArticleIDs = $update.KBArticleIDs } } return $output } # 本地直接遍历结构化数据即可,无需操作COM对象 $i = 0 $localUpdateResult | ForEach-Object { $i++ Write-Host "UpdateInfo Update No. $i" Write-Host "Title: `t`t" $_.Title Write-Host "Description: `t`t " $_.Description Write-Host "UpdateID: `t`t " $_.UpdateID Write-Host "RevisionNumber: `t`t " $_.RevisionNumber Write-Host "KBArticleIDs: `t`t " $_.KBArticleIDs Write-Host "===============================================" }
额外注意事项
- 不要尝试在本地对回传的
__ComObject做类型强转或者手动调用COM接口,这类操作跨远程会话是无效的,只会触发权限或者成员不存在的错误。 - 所有和WUA(Windows Update Agent)COM组件相关的操作,都建议在目标主机本地执行完成后再回传结果,不要回传任何COM类型的对象。
内容的提问来源于stack exchange,提问作者k_k
相关产品推荐
相关产品推荐

