如何在 C# 中获取 PSObject.Properties 的 ScriptProperty 值?

How to get ScriptProperty Values of PSObject.Properties in C#?

我正在尝试使用 'GET-PSDrive' 命令通过 PowerShell 6.0 获取服务器的驱动器信息。 运行 直接在 PowerShell 中的命令,我在输出 table 中看到 'Used' 和 'Free' 的值,但是 运行 在代码中使用相同的命令Microsoft.Powershell.Sdk 'Used' 和 'Free' 字段不可用。

我看到两个项目都列在 PSObject.Properties 数组下,但尝试访问我收到异常的值:

"There is no Runspace available to run scripts in this thread. You can provide one in the DefaultRunspace property of the System.Management.Automation.Runspaces.Runspace type. The script block you attempted to invoke was: ##"

以下是我正在使用的 POC 代码:

using (var psCon = PowerShell.Create())
{
     psCon.AddCommand("GET-PSDrive");
     var psReturn = psCon.Invoke();
     foreach (var psObj in psReturn)
     {
          var driveUsedValue = psObj.Properties["Used"].Value;
     }
}

我希望得到 属性 的值,但每次计算该值时,我都会收到一条错误消息,指出没有可用的运行空间。检查 属性 我确实看到它是一个 ScriptProperty,那么你如何获得生成的值?

属性 使用的是一个称为 ScriptProperty 的东西。这意味着当它被调用时它会运行一个脚本。我们可以通过调用看到这一点:

get-PSDrive | get-member -Name Used

这个returns

Name MemberType     Definition
---- ----------     ----------
Used ScriptProperty System.Object Used {get=## Ensure that this is a FileSystem drive...

我们可以深入挖掘并查看 运行 也

的脚本
get-PSDrive  | get-member -Name Used | select -ExpandProperty Definition

这将return

System.Object Used {
    get=## Ensure that this is a FileSystem drive
    if($this.Provider.ImplementingType -eq [Microsoft.PowerShell.Commands.FileSystemProvider]){
        $driveRoot = ([System.IO.DirectoryInfo] $this.Root).Name.Replace('\','')
        $drive = Get-CimInstance Win32_LogicalDisk -Filter "DeviceId='$driveRoot'"
        $drive.Size - $drive.FreeSpace
    };
}

这就是您得到异常的原因 There is no Runspace available to run scripts in this thread。这是因为该信息运行的脚本需要运行空间。

要解决此问题,您可以像这样将所有属性转换为注释属性

Get-PSDrive | %{
    $drive = $_
    $obj = new-object psobject
    $_.psobject.Properties.GetEnumerator() | %{
        $obj | Add-Member -MemberType NoteProperty -name $_.Name -Value $drive."$($_.name)" 
    }
    $obj
}

或者正如@mklement0 在评论中指出的那样

Get-PSDrive | Select-Object *

哪个是更好的解决方案。

它将 return 一个 PSobjects 数组,其值作为注释而不是脚本

using (var psCon = PowerShell.Create()){
    psCon.AddScript(@"
        Get-PSDrive | Select-Object *
    ");


    var psReturn = psCon.Invoke();
    foreach (var psObj in psReturn)
    {
        var driveUsedValue = psObj.Properties["Used"].Value;
    }
}

*注意该值将只是使用的字节整数。