You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VB.NET调用PowerShell时如何从错误流获取PSComputerName?

问题

我用VB.NET编写代码,通过Invoke-Command远程检查特定进程(如notepad)是否运行:

  • 找到进程时,在Output_DataAdded_ProcessRunning事件中获取PSComputerName并加入processrunning列表
  • 未找到进程时,希望在ps.Streams.Error.DataAdded事件中获取故障计算机名并加入processnotrunning列表

调试时能看到OriginInfo中的PSComputerName值,但无法直接访问该属性。此前执行Invoke-Command简单脚本、Test-Connection等命令时,可通过currentStreamRecord.TargetObject.ToString()获取计算机名,现在需要知道如何在Error_DataAdded_ProcessRunning方法中获取该计算机名。

原代码如下:

Dim test = "MYPC"
Dim processtocheck = "notepad"

Using ps As PowerShell = PowerShell.Create()
    Dim command As New PSCommand()
    command.AddCommand("Invoke-Command")
    command.AddParameter("ComputerName", test)
    command.AddParameter("ScriptBlock", ScriptBlock.Create("Get-Process " & processtocheck))
    ps.Commands = command

    Dim output As New PSDataCollection(Of PSObject)()
    AddHandler output.DataAdded, AddressOf Output_DataAdded_ProcessRunning
    AddHandler ps.Streams.Error.DataAdded, AddressOf Error_DataAdded_ProcessRunning

    Dim results As PSDataCollection(Of PSObject) = Await Task.Run(Function() ps.InvokeAsync(Of PSObject, PSObject)(Nothing, output))
End Using

Private Sub Output_DataAdded_ProcessRunning(ByVal sender As Object, ByVal e As DataAddedEventArgs)
    Dim myp As PSDataCollection(Of PSObject) = DirectCast(sender, PSDataCollection(Of PSObject))
    Dim results As Collection(Of PSObject) = myp.ReadAll()
    
    For Each result As PSObject In results
        ' add to list
        AddIfNotExists(processrunning, result.Properties("PSComputerName").Value)
    Next result
End Sub

Private Sub Error_DataAdded_ProcessRunning(ByVal sender As Object, ByVal e As DataAddedEventArgs)
    ' do something when an error is written to the error stream
    Dim streamObjectsReceived = TryCast(sender, PSDataCollection(Of ErrorRecord))
    Dim currentStreamRecord = streamObjectsReceived(e.Index)
    
    ' add to list
    ' ???
End Sub
解决方案

方法1:通过反射获取OriginInfo中的PSComputerName

ErrorRecord的InvocationInfo.OriginInfo属性包含远程计算机信息,但该属性是内部可见的,无法直接访问。可以用反射读取其中的PSComputerName值:

修改Error_DataAdded_ProcessRunning方法如下:

Private Sub Error_DataAdded_ProcessRunning(ByVal sender As Object, ByVal e As DataAddedEventArgs)
    Dim streamObjectsReceived = TryCast(sender, PSDataCollection(Of ErrorRecord))
    Dim currentStreamRecord = streamObjectsReceived(e.Index)
    
    ' 用反射获取OriginInfo的PSComputerName
    Dim originInfoProperty = currentStreamRecord.InvocationInfo.GetType().GetProperty("OriginInfo", System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance)
    If originInfoProperty IsNot Nothing Then
        Dim originInfo = originInfoProperty.GetValue(currentStreamRecord.InvocationInfo)
        If originInfo IsNot Nothing Then
            Dim psComputerNameProperty = originInfo.GetType().GetProperty("PSComputerName")
            If psComputerNameProperty IsNot Nothing Then
                Dim computerName = psComputerNameProperty.GetValue(originInfo)
                ' 添加到processnotrunning列表
                AddIfNotExists(processnotrunning, computerName)
            End If
        End If
    End If
End Sub

方法2:修改脚本块,主动返回包含计算机名的错误信息(更可靠)

避免依赖内部属性,直接在远程脚本块中捕获错误,手动构造包含计算机名的对象返回:

修改Invoke-Command的脚本块部分:

command.AddParameter("ScriptBlock", ScriptBlock.Create($"
    try {{
        Get-Process {processtocheck}
    }} catch {{
        # 返回包含计算机名和错误信息的对象
        [PSCustomObject]@{
            'PSComputerName' = $env:COMPUTERNAME
            'ErrorMessage' = $_.Exception.Message
        }
    }}
"))

然后修改错误事件处理(此时错误流中会包含我们自定义的对象,直接读取PSComputerName即可):

Private Sub Error_DataAdded_ProcessRunning(ByVal sender As Object, ByVal e As DataAddedEventArgs)
    Dim streamObjectsReceived = TryCast(sender, PSDataCollection(Of ErrorRecord))
    Dim currentStreamRecord = streamObjectsReceived(e.Index)
    
    ' 从TargetObject中获取自定义的PSComputerName
    Dim errorObj = TryCast(currentStreamRecord.TargetObject, PSObject)
    If errorObj IsNot Nothing Then
        Dim computerName = errorObj.Properties("PSComputerName")?.Value
        If computerName IsNot Nothing Then
            AddIfNotExists(processnotrunning, computerName)
        End If
    End If
End Sub

方法2更稳定,因为不依赖PowerShell内部未公开的属性,避免后续版本更新导致代码失效。

内容的提问来源于stack exchange,提问作者Darren Rose

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 02:15:36