使用Get-WmiObject Win32_Product远程卸载应用遇致命错误求助
远程卸载工具使用Get-WmiObject Win32_Product超时报错的问题分析与解决
问题原因分析
- Win32_Product类的固有缺陷:调用这个WMI类时,系统会自动对所有已安装的MSI包执行一致性检查(验证文件完整性、修复损坏项),当目标机器安装了大量MSI包时,这个过程会耗时极长,甚至触发系统级的资源耗尽或超时。
- Get-WmiObject的默认超时限制:该命令默认的连接超时为30秒,操作超时也有隐性限制,当WMI查询无法在限定时间内完成所有包的枚举与检查时,就会抛出无详细说明的致命错误。
解决办法
1. 替换Win32_Product,改用注册表读取已安装程序
Win32_Product不仅运行缓慢,还可能意外修改系统状态,读取注册表是更安全高效的方案。已安装程序的信息存储在以下注册表路径:
- 32位程序:
HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* - 64位程序:
HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*
远程读取注册表的示例代码:
$ComputerName = Read-Host -Prompt 'Input the computer name' # 远程读取注册表获取已安装程序 $regPaths = @( "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" ) $allApps = Invoke-Command -ComputerName $ComputerName -ScriptBlock { param($Paths) Get-ItemProperty -Path $Paths | Where-Object { $_.DisplayName -ne $null } | Select-Object DisplayName } -ArgumentList $regPaths $allApps | Select-Object DisplayName | Out-GridView -Title "All apps on destination Computer" $Name = Read-Host -Prompt 'Input name of the application (has to be exact name)' $appToUninstall = Invoke-Command -ComputerName $ComputerName -ScriptBlock { param($AppName) $uninstallPaths = @( "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" ) Get-ItemProperty -Path $uninstallPaths | Where-Object { $_.DisplayName -eq $AppName -and $_.UninstallString -ne $null } } -ArgumentList $Name if ($appToUninstall) { # 处理卸载字符串,适配msiexec参数格式 $uninstallCmd = $appToUninstall.UninstallString -replace '^"([^"]+)"(.*)', '$1 $2' -replace '/I', '/X' Invoke-Command -ComputerName $ComputerName -ScriptBlock { param($Cmd) Start-Process -FilePath "msiexec.exe" -ArgumentList $Cmd -Wait -NoNewWindow } -ArgumentList $uninstallCmd "The removal was successful" } else { "$Name is not installed on $ComputerName" } Start-Sleep -Seconds 10
2. 调整WMI查询的超时参数(若坚持使用WMI)
如果必须保留WMI方式,可以使用PowerShell推荐的Get-CimInstance替代Get-WmiObject,它的超时设置更直观:
$ComputerName = Read-Host -Prompt 'Input the computer name' # 设置操作超时为300秒(5分钟) $allProducts = Get-CimInstance -ClassName Win32_Product -ComputerName $ComputerName -OperationTimeoutSec 300 $allProducts | Select-Object Name | Out-GridView -Title "All apps on destination Computer" $Name = Read-Host -Prompt 'Input name of the application (has to be exact name)' $Application = $allProducts | Where-Object {$_.Name -eq $Name} if ($Application) { $Application | Invoke-CimMethod -MethodName Uninstall "The removal was successful" } else { "$Name is not installed on $ComputerName" } Start-Sleep -Seconds 10
3. 优化代码逻辑,避免重复查询
原代码两次调用WMI查询,会重复触发MSI一致性检查,可将第一次查询结果缓存,后续直接从缓存中筛选:
$ComputerName = Read-Host -Prompt 'Input the computer name' # 仅执行一次WMI查询并缓存结果 $allProducts = Get-CimInstance -ClassName Win32_Product -ComputerName $ComputerName -OperationTimeoutSec 300 $allProducts | Select-Object Name | Out-GridView -Title "All apps on destination Computer" $Name = Read-Host -Prompt 'Input name of the application (has to be exact name)' $Application = $allProducts | Where-Object {$_.Name -eq $Name} if ($Application) { $Application | Invoke-CimMethod -MethodName Uninstall "The removal was successful" } else { "$Name is not installed on $ComputerName" } Start-Sleep -Seconds 10
内容的提问来源于stack exchange,提问作者Minus
相关产品推荐
相关产品推荐

