Windows Win32_产品的替代品?

Windows Win32_产品的替代品?,windows,powershell,wmi-query,Windows,Powershell,Wmi Query,在反复查询Win32\u产品以查找软件版本后,我无法理解为什么结果如此缓慢。比查询Win32\u服务或Win32\u进程慢15倍之多。所以来这里看看我是否遗漏了什么,我发现其他人也报告了同样的问题,这就解释了原因 查找已安装软件最常用的替代方法是查询一个或三个注册表项。这将是我的第一个解决方案,只是我的公司还没有将服务器配置为接受PSRemoting。任何reg查询都只返回Kerberos身份验证错误。我可以在单个服务器上启用PSRemoting,但我的团队支持30K系统。所以这个解决方案已经过

在反复查询
Win32\u产品
以查找软件版本后,我无法理解为什么结果如此缓慢。比查询
Win32\u服务
Win32\u进程
慢15倍之多。所以来这里看看我是否遗漏了什么,我发现其他人也报告了同样的问题,这就解释了原因

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


归根结底,我们正在从v升级Symantec Endpoint Protection。11至5。12,我想要一个简单的检查,找出服务器上安装了什么版本。除了查找
Win32\u产品
和注册表查询之外,还有其他方法可以查找版本吗?

我远程使用注册表,而不使用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()
    }
}  
函数获取远程软件{
获取RemoteSoftware-ComputerName服务器1
这显示了服务器1上安装的软件。
#>
param(
[参数(必需=$true,ValueFromPipelineByPropertyName=$true)][string[]
#指定要连接到的计算机名
$ComputerName
)
过程{
foreach($ComputerName中的计算机)
{
#开放式远程基地
$reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)
#检查是否有64位注册表项
$keyRootSoftware=$reg.OpenSubKey(“软件”)
[bool]$is64=($keyRootSoftware.GetSubKeyNames()|?{$eq'WOW6432Node'}|度量对象)。计数
$keyRootSoftware.Close()
#将它们的所有键放入一个列表中
$softwareKeys=@()
如果(64美元){
$pathUninstall64=“软件\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall”
$keyUninstall64=$reg.OpenSubKey($pathUninstall64)
$keyUninstall64.GetSubKeyNames()|%{
$softwareKeys+=$pathUninstall64+“\\\”+$_
}
$keydunnstall64.Close()
}
$pathUninstall32=“软件\\Microsoft\\Windows\\CurrentVersion\\Uninstall”
$keyUninstall32=$reg.OpenSubKey($pathUninstall32)
$keyUninstall32.GetSubKeyNames()|%{
$softwareKeys+=$pathUninstall32+“\\\”+$_
}
$keydunnstall32.Close()
#从所有钥匙中获取信息
$softwareKeys |%{
$subkey=$reg.OpenSubKey($\ux)
if($subkey.GetValue(“DisplayName”)){
$installDate=$null
if($subkey.GetValue(“InstallDate”)-match“/){
$installDate=获取日期$subkey.GetValue(“installDate”)
}
elseif($subkey.GetValue(“InstallDate”).length-eq 8){
$installDate=获取日期$subkey.GetValue(“installDate”)。插入(6“.”。插入(4“.”)
}
新对象PSObject-属性@{
ComputerName=$Computer
Name=$subkey.GetValue(“DisplayName”)
Version=$subKey.GetValue(“DisplayVersion”)
供应商=$subkey.GetValue(“发布者”)
卸载字符串=$subkey.GetValue(“卸载字符串”)
InstallDate=$InstallDate
}
}
$subkey.Close()
}
$reg.Close()
}
}  
}

我建议阅读为什么Win32_产品不好以及替代品,但引用相关章节:

…事实证明,查询Win32_产品的操作具有 可能会对您的系统造成严重破坏。这就是我们的本质 KB974524

Win32_产品类未进行查询优化。查询,例如 “从Win32_产品中选择*,其中(名称如'Sniffer%')”需要WMI 使用MSI提供程序枚举所有已安装的产品和 然后按顺序分析完整列表以处理“where”子句:

此过程将启动已安装软件包的一致性检查,以及 然后对装置进行验证和维修。如果你有 使用Win32_产品类的应用程序,您应该 请与供应商联系以获取不使用此选项的更新版本 班级

在Windows Server 2003、Windows Vista和更新的操作系统上 系统,查询Win32_产品将触发Windows Installer 执行一致性检查以验证应用程序的运行状况。 此一致性检查可能导致进行修复安装。你 可以通过检查Windows应用程序事件日志来确认这一点。你 将在每次查询类时看到以下事件 安装的每个产品:


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
    }
}
}  
}
函数Get-WmiCustom2([string]$computername,[string]$namespace,[string]$class,[int]$timeout=15,[string]$whereclause='') { #MSFT的Daniele Muscetta提供的函数Get-WMICustom2 #这是添加where子句参数的修改版本,可选 #原始功能:http://blogs.msdn.com/b/dmuscett/archive/2009/05/27/get_2d00_wmicustom.aspx $ConnectionOptions=新对象System.Management.ConnectionOptions $EnumerationOptions=新对象System.Management.EnumerationOptions $timeoutseconds=新时间跨度-秒$timeout $EnumerationOptions.set_timeout($timeoutseconds) $assembledpath=“\\”+$com
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
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
    }
}
}  
}
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