使用 .NET(PowerShell 或 .NET)获取 BCD 条目

Sup*_*JMN 2 .net c# powershell wmi bcdedit

我正在创建一个应用程序来分析启动配置数据(BCD)中的条目。

我尝试过使用 PowerShell,但它似乎没有提供任何 cmdlet 来处理它。因此,我又回到了 .NET,特别是 C#。

我想要有东西像这样获取 BCD 条目

var entries = bcd.GetEntries();
Run Code Online (Sandbox Code Playgroud)

条目是IList<BcdEntry>

class BcdEntry
{
    public string Name {get; set; }
    IDictionary<string, IList<string>> Properties { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

问题是我不知道如何获取条目。调用 BCDEdit 是一种可能性,但它需要解析命令的输出,这是一项繁琐的任务。

我希望你能为我的问题想出一个解决方案。

mkl*_*nt0 5

bcdedit.exe /enum将输出解析为自定义对象列表的PSv4+ 解决方案:

# IMPORTANT: bcdedit /enum requires an ELEVATED session.
$bcdOutput = (bcdedit /enum) -join "`n" # collect bcdedit's output as a *single* string

# Initialize the output list.
$entries = New-Object System.Collections.Generic.List[pscustomobject]]

# Parse bcdedit's output.
($bcdOutput -split '(?m)^(.+\n-)-+\n' -ne '').ForEach({
  if ($_.EndsWith("`n-")) { # entry header 
    $entries.Add([pscustomobject] @{ Name = ($_ -split '\n')[0]; Properties = [ordered] @{} })
  } else {  # block of property-value lines
    ($_ -split '\n' -ne '').ForEach({
      $propAndVal = $_ -split '\s+', 2 # split line into property name and value
      if ($propAndVal[0] -ne '') { # [start of] new property; initialize list of values
        $currProp = $propAndVal[0]
        $entries[-1].Properties[$currProp] = New-Object Collections.Generic.List[string]
      }
      $entries[-1].Properties[$currProp].Add($propAndVal[1]) # add the value
    })
  }
})

# Output a quick visualization of the resulting list via Format-Custom
$entries | Format-Custom
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 正如LotPing观察到的那样,

    • bcdedit.exe输出部分本地化;具体而言,包括以下几项:
      • 条目标题(例如,英语Windows Boot ManagerAdministrador de arranque de Windows西班牙语)
      • 奇怪的是,还有用英语命名的财产名称identifier(例如,Identificador用西班牙语)。
    • 为了简洁起见,代码没有尝试将本地化名称映射到美式英语对应名称,但这是可以做到的。

    • bcdedit此外,随此 ServerFault 问题(重复)发布的示例输出表明,可能存在太长的属性名称,以至于它们会遇到它们的值,而不会插入空格,也不会被截断。
      如果这不仅仅是发布的产物,那么将需要更多的工作来处理这种情况;本文包含属性名称列表。

  • [pscustomobject]使用实例而不是自定义BcdEntry类的实例;在 PSv5+ 中,您可以直接在 PowerShell 中创建这样的自定义类。

  • 属性值全部捕获为字符串值,收集在[List[string]]列表中(即使只有 1 个值);需要进行额外的工作才能将它们解释为特定类型;
    例如,[int] $entries[1].Properties['allowedinmemorysettings'][0]将字符串转换'0x15000075'为整数。


输入/输出示例:

给定bcdedit.exe /enum这样的输出...

Windows Boot Manager
--------------------
identifier              {bootmgr}
device                  partition=C:
displayorder            {current}
                        {e37fc869-68b0-11e8-b4cf-806e6f6e6963}
description             Windows Boot Manager
locale                  en-US
inherit                 {globalsettings}
default                 {current}
resumeobject            {9f3d8468-592f-11e8-a07d-e91e7e2fad8b}
toolsdisplayorder       {memdiag}
timeout                 0

Windows Boot Loader
-------------------
identifier              {current}
device                  partition=C:
path                    \WINDOWS\system32\winload.exe
description             Windows 10
locale                  en-US
inherit                 {bootloadersettings}
recoverysequence        {53f531de-590e-11e8-b758-8854872f7fe5}
displaymessageoverride  Recovery
recoveryenabled         Yes
allowedinmemorysettings 0x15000075
osdevice                partition=C:
systemroot              \WINDOWS
resumeobject            {9f3d8468-592f-11e8-a07d-e91e7e2fad8b}
nx                      OptIn
bootmenupolicy          Standard
Run Code Online (Sandbox Code Playgroud)

...上面的命令会产生这样的结果:

# IMPORTANT: bcdedit /enum requires an ELEVATED session.
$bcdOutput = (bcdedit /enum) -join "`n" # collect bcdedit's output as a *single* string

# Initialize the output list.
$entries = New-Object System.Collections.Generic.List[pscustomobject]]

# Parse bcdedit's output.
($bcdOutput -split '(?m)^(.+\n-)-+\n' -ne '').ForEach({
  if ($_.EndsWith("`n-")) { # entry header 
    $entries.Add([pscustomobject] @{ Name = ($_ -split '\n')[0]; Properties = [ordered] @{} })
  } else {  # block of property-value lines
    ($_ -split '\n' -ne '').ForEach({
      $propAndVal = $_ -split '\s+', 2 # split line into property name and value
      if ($propAndVal[0] -ne '') { # [start of] new property; initialize list of values
        $currProp = $propAndVal[0]
        $entries[-1].Properties[$currProp] = New-Object Collections.Generic.List[string]
      }
      $entries[-1].Properties[$currProp].Add($propAndVal[1]) # add the value
    })
  }
})

# Output a quick visualization of the resulting list via Format-Custom
$entries | Format-Custom
Run Code Online (Sandbox Code Playgroud)

要以编程方式处理条目

foreach($entry in $entries) { 
  # Get the name.
  $name = $entry.Name
  # Get a specific property's value.
  $prop = 'device'
  $val = $entry.Properties[$prop] # $val is a *list*; e.g., use $val[0] to get the 1st item
}
Run Code Online (Sandbox Code Playgroud)

注意:$entries | ForEach-Object { <# work with entry $_ #> },即使用管道也是一种选择,但如果条目列表已经在内存中,则循环速度foreach更快。