首页 文章

在x86上下文中运行时,PowerShell会返回已安装软件的不准确结果

提问于
浏览
2

我编写了以下脚本来检查应用程序名称并回显一些属性(如果已安装):

$input = "Microsoft Office Professional"
$appName = "*" + $input + "*"

$responseX64 = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |  Where-Object {$_.DisplayName -like $appName} | Select-Object @{Expression={$_.DisplayName + "|" + $_.DisplayVersion +"|x64"}} | Sort-Object -Unique | ft -HideTableHeaders
$responseX86 = Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |  Where-Object {$_.DisplayName -like $appName} | Select-Object @{Expression={$_.DisplayName + "|" + $_.DisplayVersion +"|x86"}} | Sort-Object -Unique | ft -HideTableHeaders

If (([string]::IsNullOrEmpty($responseX64)) -and ([string]::IsNullOrEmpty($responseX86)))
{
    Write-Output "No matches found."
    exit
}
else 
{
    Write-Output $responseX64
    Write-Output $responseX86
}

为了完整性,我正在检查x86和x64 Uninstall注册表项 . 当我在x64 PowerShell会话中运行它并正确返回我有一个x86安装的Microsoft Office时,这可以正常工作:

Microsoft Office Professional Plus 2016 | 16.0.4266.1001 | x86

但是,当我在PowerShell的x86会话中在同一台机器上运行它(因为它将由我的x86管理代理程序运行)时,我得到以下返回:

Microsoft Office Professional Plus 2016 | 16.0.4266.1001 | x64 Microsoft Office Professional Plus 2016 | 16.0.4266.1001 | x86

我已经在HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Uninstall下仔细检查了我的注册表项,以确保没有Office的某些x64工件,但没有任何内容 . 如何修改此脚本,以便在x86上下文中运行时返回准确的结果?

2 回答

  • 3

    您的问题是,对于32位进程,以下键路径引用相同的位置,即32位应用程序的注册表视图:

    # To both 64- and 32-bit processes: The registry view for 32-bit applications.
    HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
    
    # To 32-bit processes: same as above
    # To 64-bit processes: the 64-bit view of the registry.
    HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*
    

    换句话说: your code, when run in a 32-bit process, does the same thing twice .


    Using just a registry key path won't allow a 32-bit process to see the 64-bit registry.

    但是,有 workarounds

    • 通过目录 $env:WinDir\SysNative 将PowerShell的64位实例作为外部进程调用 - 见下文 .

    • 直接使用.NET类型,如this answer所示 .


    使用脚本块 { ... } 从(32位)PowerShell调用64位PowerShell会自动以CLIXML格式序列化64位实例的输出对象,这使得调用实例能够以合理的保真度自动反序列化它们[1],以便您的原始过滤命令应该工作:

    # Works only in a 32-bit process.
    & $env:WINDIR\SysNative\WindowsPowerShell\v1.0\powershell.exe {
      Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*
    } | 
      Where-Object {$_.DisplayName -like $appName} | 
        Select-Object @{Expression={$_.DisplayName + "|" + $_.DisplayVersion +"|x64"}} |
          Sort-Object -Unique
    

    要确定您的脚本是否在32位进程中运行,请使用以下命令:

    $is32Bit = ${env:ProgramFiles(x86)} -eq ${env:ProgramFiles} -or 
                 -not ${env:ProgramFiles(x86)}
    

    -or -not ${env:ProgramFiles(x86)} 部分检测到在纯32位Windows版本上运行的情况,但请注意,显然,那里不存在64位定义 .


    [1]除了一些众所周知的类型之外,输入对象被反序列化为[PSCustomObject]实例,其静态属性反映了原始对象的属性值 .

  • 2

    要添加@mklement0的正确答案,可能一个小的测试函数可以帮助确定Windows位数而不是当前的PowerShell进程位数:

    function Get-Architecture {
        # What architecture does Windows use
        $windowsBitness = switch ([Environment]::Is64BitOperatingSystem) {   # needs .NET 4
            $true  { 64; break }
            $false { 32; break }   
            default { 
                (Get-WmiObject -Class Win32_OperatingSystem).OSArchitecture -replace '\D+', '' 
                # Or do any of these:
                # ((Get-WmiObject -Class Win32_ComputerSystem).SystemType -replace '\D+', '') -replace '86', '32'
                # (Get-WmiObject -Class Win32_Processor).AddressWidth   # slow...
            }
        }
    
        # What architecture does this PowerShell process use
        $processBitness = [IntPtr]::Size * 8
        # Or do any of these:
        # $processBitness = ($env:PROCESSOR_ARCHITECTURE -replace '\D+]', '') -replace '86', '32'
        # $processBitness = if ([Environment]::Is64BitProcess) { 64 } else { 32 }
    
        # Return the info as object
        return New-Object -TypeName PSObject -Property @{
            'ProcessArchitecture' = "{0}-bit" -f $processBitness
            'WindowsArchitecture' = "{0}-bit" -f $windowsBitness
        }
    }
    

    可能的回报是:

    64-bit PowerShell on 64-bit Windows

    ProcessArchitecture WindowsArchitecture
    ------------------- -------------------
    64-bit              64-bit
    

    32-bit PowerShell on 64-bit Windows

    ProcessArchitecture WindowsArchitecture
    ------------------- -------------------
    32-bit              64-bit
    

    32-bit PowerShell on 32-bit Windows

    ProcessArchitecture WindowsArchitecture
    ------------------- -------------------
    32-bit              32-bit
    

相关问题