Win32_Product的替代品?

问题描述 投票:4回答:4

[反复查询Win32_Product以找到软件版本后,我不明白为什么结果如此之慢。比查询Win32_serviceWin32_process慢15倍。因此,来到这里查看我是否缺少某些内容,我发现其他人也报告了相同的问题,并且此article解释了原因。

查找已安装软件的最常建议的替代方法是查询一个或三个注册表项。那将是我的第一个解决方案,除了我的公司尚未将服务器配置为接受PSRemoting。任何reg查询只会返回Kerberos身份验证错误。我可以在单个服务器上启用PSRemoting,但是我的团队支持30K系统。这样就解决了。

最重要的是,我们正在将Symantec Endpoint Protection从v。11升级到v。12,我想简单检查一下服务器上安装了什么版本。除了Win32_Product和注册表查询,是否还有其他选择来查找版本?

windows powershell wmi-query
4个回答
4
投票

我无需PSRemoting即可远程使用注册表。这是我每天编写并用于查询软件的函数。

Function Get-RemoteSoftware{
<#
.SYNOPSIS 
Displays all software listed in the registry on a given computer.

.DESCRIPTION
Uses the SOFTWARE registry keys (both 32 and 64bit) to list the name, version, vendor, and uninstall string for each software entry on a given computer.

.EXAMPLE
C:\PS> Get-RemoteSoftware -ComputerName SERVER1
This shows the software installed on SERVER1.
#>

param (
    [Parameter(mandatory=$true,ValueFromPipelineByPropertyName=$true)][string[]]
    # Specifies the computer name to connect to
    $ComputerName
)

Process {
    foreach ($Computer in $ComputerName)
    {
        #Open Remote Base
        $reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)

        #Check if it's got 64bit regkeys
        $keyRootSoftware = $reg.OpenSubKey("SOFTWARE")
        [bool]$is64 = ($keyRootSoftware.GetSubKeyNames() | ? {$_ -eq 'WOW6432Node'} | Measure-Object).Count
        $keyRootSoftware.Close()

        #Get all of they keys into a list
        $softwareKeys = @()
        if ($is64){
            $pathUninstall64 = "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
            $keyUninstall64 = $reg.OpenSubKey($pathUninstall64)
            $keyUninstall64.GetSubKeyNames() | % {
                $softwareKeys += $pathUninstall64 + "\\" + $_
            }
            $keyUninstall64.Close()
        }
        $pathUninstall32 = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
        $keyUninstall32 = $reg.OpenSubKey($pathUninstall32)
        $keyUninstall32.GetSubKeyNames() | % {
            $softwareKeys += $pathUninstall32 + "\\" + $_
        }
        $keyUninstall32.Close()

        #Get information from all the keys
        $softwareKeys | % {
            $subkey=$reg.OpenSubKey($_)
            if ($subkey.GetValue("DisplayName")){
                $installDate = $null
                if ($subkey.GetValue("InstallDate") -match "/"){
                    $installDate = Get-Date $subkey.GetValue("InstallDate")
                }
                elseif ($subkey.GetValue("InstallDate").length -eq 8){
                    $installDate = Get-Date $subkey.GetValue("InstallDate").Insert(6,".").Insert(4,".")
                }
                New-Object PSObject -Property @{
                    ComputerName = $Computer
                    Name = $subkey.GetValue("DisplayName")
                    Version = $subKey.GetValue("DisplayVersion")
                    Vendor = $subkey.GetValue("Publisher")
                    UninstallString = $subkey.GetValue("UninstallString")
                    InstallDate = $installDate
                }
            }

            $subkey.Close()
        }
        $reg.Close()
    }
}  

}


1
投票

该Get-RemoteSoftware的工作原理很好-假设远程注册表服务在远程系统上启动。如果没有,您将得到一个错误。我总是检查它是否已启动,如果没有启动,则在启动后将其停止。我想知道这是否就是为什么原本很棒的函数会被否决的原因。

这里是经过稍微修改的版本,它将检查并启动远程注册表服务,并在完成后停止。

Function Get-WmiCustom2([string]$computername,[string]$namespace,[string]$class,[int]$timeout=15,[string]$whereclause='') 
{
#Function Get-WMICustom2 by MSFT's Daniele Muscetta 
#This is a modified version to add where clause parameter, optional
#Original function: http://blogs.msdn.com/b/dmuscett/archive/2009/05/27/get_2d00_wmicustom.aspx

$ConnectionOptions = new-object System.Management.ConnectionOptions
$EnumerationOptions = new-object System.Management.EnumerationOptions
$timeoutseconds = new-timespan -seconds $timeout
$EnumerationOptions.set_timeout($timeoutseconds)
$assembledpath = "\\" + $computername + "\" + $namespace

$Scope = new-object System.Management.ManagementScope $assembledpath, $ConnectionOptions

try {
    $Scope.Connect()
} catch {
    $result="Error Connecting " + $_
    return $Result 
}

$querystring = "SELECT * FROM " + $class + " " + $whereclause
$query = new-object System.Management.ObjectQuery $querystring
$searcher = new-object System.Management.ManagementObjectSearcher
$searcher.set_options($EnumerationOptions)
$searcher.Query = $querystring
$searcher.Scope = $Scope

trap { $_ } $result = $searcher.get()

return $result
}

Function Get-RemoteSoftware{
<#
.SYNOPSIS 
Displays all software listed in the registry on a given computer.

.DESCRIPTION
Uses the SOFTWARE registry keys (both 32 and 64bit) to list the name, version, vendor, and uninstall string for each software entry on a given computer.

.EXAMPLE
C:\PS> Get-RemoteSoftware -ComputerName SERVER1
This shows the software installed on SERVER1.
#>

param (
[Parameter(mandatory=$true,ValueFromPipelineByPropertyName=$true)][string[]]
# Specifies the computer name to connect to
$ComputerName
)

Process {
    foreach ($Computer in $ComputerName)
    {
        $ChangeStateBack=$False
        $RemoteRegistryObj=""
        $ServiceWMIObj=@(get-wmicustom2 -class "win32_service" -namespace "root\cimv2" -whereclause "WHERE name='RemoteRegistry'" -computername $computername –timeout 60 -erroraction stop)
        if ($ServiceWMIObj.Count -gt 0) {
        $RemoteRegistryObj =  $ServiceWMIObj[0]
        if ($RemoteRegistryObj.State -ne 'Running') {
                $ChangeStateBack=$true
                $RemoteRegistryObj.InvokeMethod("StartService",$null) | Out-Null
                Start-Sleep -m 1800     
                #give it a chance to actually start. 1.5 second delay
        }
}
    #Open Remote Base
    $reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)

    #Check if it's got 64bit regkeys
    $keyRootSoftware = $reg.OpenSubKey("SOFTWARE")
    [bool]$is64 = ($keyRootSoftware.GetSubKeyNames() | ? {$_ -eq 'WOW6432Node'} | Measure-Object).Count
    $keyRootSoftware.Close()

    #Get all of they keys into a list
    $softwareKeys = @()
    if ($is64){
        $pathUninstall64 = "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
        $keyUninstall64 = $reg.OpenSubKey($pathUninstall64)
        $keyUninstall64.GetSubKeyNames() | % {
            $softwareKeys += $pathUninstall64 + "\\" + $_
        }
        $keyUninstall64.Close()
    }
    $pathUninstall32 = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
    $keyUninstall32 = $reg.OpenSubKey($pathUninstall32)
    $keyUninstall32.GetSubKeyNames() | % {
        $softwareKeys += $pathUninstall32 + "\\" + $_
    }
    $keyUninstall32.Close()

    #Get information from all the keys
    $softwareKeys | % {
        $subkey=$reg.OpenSubKey($_)
        if ($subkey.GetValue("DisplayName")){
            $installDate = $null
            if ($subkey.GetValue("InstallDate") -match "/"){
                $installDate = Get-Date $subkey.GetValue("InstallDate")
            }
            elseif ($subkey.GetValue("InstallDate").length -eq 8){
                $installDate = Get-Date $subkey.GetValue("InstallDate").Insert(6,".").Insert(4,".")
            }
            New-Object PSObject -Property @{
                ComputerName = $Computer
                Name = $subkey.GetValue("DisplayName")
                Version = $subKey.GetValue("DisplayVersion")
                Vendor = $subkey.GetValue("Publisher")
                UninstallString = $subkey.GetValue("UninstallString")
                InstallDate = $installDate
            }
        }

        $subkey.Close()
    }
    $reg.Close()
    if ($ChangeStateBack){
                            $RemoteRegistryObj.InvokeMethod("StopService",$null)  | Out-Null
    }
}
}  
}

这使用了MSFT的某人编写的自定义WMI获取包装器,因此,如果完整复制此摘要,它将按原样工作。您可以将其修改回标准的get-wmiobject函数,但是没有内置超时。在某些(并非十分罕见)的情况下,远程WMI响应器将无限期挂起并使用默认WMI,因此这会增加超时。-Dane


0
投票

我建议阅读this scripting guy article,以了解Win32_Product为什么不好以及其他原因。我通常使用Win32Reg_AddRemovePrograms,因为我们使用的是安装此类的SCCM。如果您不使用SCCM,请使用注册表查询(例如@Chris N发布的注册表查询)。

PS:\>Measure-Command {gwmi win32reg_addremoveprograms}
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 162
Ticks             : 1623758
TotalDays         : 1.87934953703704E-06
TotalHours        : 4.51043888888889E-05
TotalMinutes      : 0.00270626333333333
TotalSeconds      : 0.1623758
TotalMilliseconds : 162.3758

0
投票

实际上有后续行动嘿!脚本专家文章Use PowerShell to Find Installed Software讨论了获取信息的其他更有效的方法。简要地说,使用以下两个命令之一:

Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |
    Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
    Format-Table –AutoSize
Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
    Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
    Format-Table –AutoSize
© www.soinside.com 2019 - 2024. All rights reserved.