【问题标题】:Uninstalled application still shows in win32_product卸载的应用程序仍显示在 win32_product 中
【发布时间】:2021-08-07 02:12:50
【问题描述】:

我有一个应用程序似乎留下了一团糟。我已经卸载了该应用程序,它似乎已经正常进行了。 不再显示在程序和功能中。注册表位置中没有任何内容: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer
搜索时什么也没有。

当我尝试安装较新版本时,我收到需要先删除旧版本的错误。

当我使用 PS 查看 Win32_Product 时,我终于发现了一些东西:

Get-WmiObject Win32_Product | Sort-Object Name | Format-Table IdentifyingNumber, Name, LocalPackage

问题是,我怎样才能用脚本把它从这里弄出来?我无法运行卸载,出现错误。我已经能够使用 MSI Cleanup Utility 来删除,但我希望能够做一些更自动化的事情。估计该状态下大约有 200 台机器。

【问题讨论】:

  • 是哪个应用程序?
  • 赛门铁克 WSS 代理

标签: powershell wmi


【解决方案1】:

有一些很好的理由从不使用Win32_Product。如果你用谷歌搜索,有很多解释,但这是第一个热门,Please Stop Using Win32_Product To Find Installed Software. Alternatives Inside!

当然,这不是你的问题,Win32_Product 只是你找到它的方式。程序安装数据可能位于不同的位置。

试试看:

  1. 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\'
  2. 'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\'
  3. 'Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages\'

Gallery 上有一个很棒的脚本,叫做 Get-RemoteProgram.ps1,它打包了一个同名的函数,所以将它点源到您的会话中,例如:

. <Path>\Get-RemoteProgram.ps1

进入后,您可以搜索该程序并在输出中包含注册表路径。帮助文件中有很多示例,但类似:

Get-RemoteProgram -ComputerName $env:computername -IncludeProgram ^Office -ProgramRegExMatch -DisplayRegPath

一旦您知道位置,我就会寻找 UninstallString 值。如果是的话,我会考虑如何让它静默运行,如果它是一个 MSI 包应该很简单。完成后,只需在其周围包裹一些 PowerShell 代码即可调用并监控它直至完成。

评论更新:

显然,我很难从远处弄清楚这一点。我在上面发布是因为它会在注册表中找到一些东西。部分原因是您没有列出 Wow6432... 位置。

鉴于我之前的陈述,我不会尝试自己测试 Win32_Product。但是,我的下一步是弄清楚 Win32_Product 发现了什么。在这种情况下,我将从Process Monitor 开始。这将需要一些工作,但可能会阐明 Win32_Process 的发现。

我可以建议的另一件事是在另一个系统上观察软件的全新安装。通过对注册表进行快照,或许还可以在之前和之后列出目录列表,您可能会发现额外的面包屑。

您还可以使用程序的辅助实例来收集卸载字符串,然后尝试在相关系统上运行它以查看会发生什么。

【讨论】:

  • 听起来像一个花哨的模块!会看看它:)
  • 感谢您的回复。问题是,它不在任何地方的注册表中。我从来没有见过这样的事情。使用 MSI 卸载时出现错误,尝试升级时出现错误。我可以判断这台机器是否已启动程序的唯一方法是查询 Win32_Product。
【解决方案2】:

小猪支持史蒂文的回答,你可以使用类似这样的东西:

Function Uninstall-Software {
[CmdletBinding()]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$false,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true)]
        [Alias('cn','name')]
        [string[]]$ComputerName,

        [Parameter(Mandatory=$false)]
        [switch]$Quiet
    )
Begin{
$Date_Now = Get-Date
}
Process{
    if($Quiet){
        foreach($Computer in $ComputerName){
            try{

            $PSSession = New-PSSession -ComputerName $Computer -ErrorAction Stop

        [array]$Software_List = Invoke-Command -ScriptBlock { 
                                    Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*",
                                                     "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"} -Session $PSSession  
        [array]$Software_List = $Software_List | Sort-Object -Property DisplayName -Unique
            $(for($i=0; $i -lt $Software_List.Count; $i++){
            [PSCustomObject]@{
                'Display Name' ="${i}.) $($Software_List[$i].DisplayName)"
                '  Version  '  = $Software_List[$i].DisplayVersion
            }
        } ) | Out-Host

$QS = Read-Host -Prompt "Enter Number of software to uninstall"
$QS = $QS -split ','
    if([string]::IsNullOrEmpty("$QS") -eq $true){"Null string"; break}

   foreach ($Q in $QS) {
        if($Software_List[$Q].QuietUninstallString -eq $null){
            
            $Quiet_Switch = '/quiet'
            $Uninstall_String = $Software_List[$Q].UninstallString

            Invoke-Command -ScriptBlock { & cmd /c $Using:Uninstall_String $Using:Quiet_Switch /norestart } -Session $PSSession -EnableNetworkAccess
        }
        else{
            
            $Uninstall_String = $Software_List[$Q].QuietUninstallString 

            Invoke-Command -ScriptBlock { & cmd /c $Using:Uninstall_String /norestart } -Session $PSSession -EnableNetworkAccess
        }

        #& cmd /c $Uninstall_String $Quiet_Switch /norestart 
        
            }
        } Catch [System.Management.Automation.RuntimeException] {
                $Error[0].Message.Split('.')[1].Trim()
        }
    }
}
    Else{
        foreach($Computer in $ComputerName){
            try{

        $PSSession = New-PSSession -ComputerName $Computer -ErrorAction Stop

        [array]$Software_List = Invoke-Command -ScriptBlock { 
                                    Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*",
                                                     "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"} -Session $PSSession 
        [array]$Software_List = $Software_List | Sort-Object -Property DisplayName -Unique
            $(for($i=0; $i -lt $Software_List.Count; $i++){
            [PSCustomObject]@{
                'Display Name' ="${i}.) $($Software_List[$i].DisplayName)"
                '  Version  '  = $Software_List[$i].DisplayVersion
            }
        } ) | Out-Host

$QS = Read-Host -Prompt "Enter Number of software to uninstall"
$QS = $QS -split ','
    if([string]::IsNullOrEmpty("$QS") -eq $true){"Null string"; break}
    
        foreach($Q in $QS){
            $Uninstall_String = $Software_List[$Q].UninstallString
            & cmd /c $Uninstall_String /norestart 

                    }
                } Catch [System.Management.Automation.RuntimeException] {
                    $Error[0].Message.Split('.')[1].Trim()
                }
        
            }
        }
    }
}

调用该函数为您提供了 2 个选项。

  • 互动:Uninstall-Software
  • 非交互:Uninstall-Software -Quiet

您可以想象,交互式选择不适用于远程计算机,但使用-Quiet 会。

  • Uninstall-Software -ComputerName RemoteComputer -Quiet

这是我不久前制作的一个脚本,但从未完成,所以到处都是。大部分工作,但可以使用一些严肃的工作。我只是不再关心它了。

运行该功能将从注册表中提取的已安装软件列表中选择一个数字。因此,您所要做的就是选择要卸载的软件数量,或选择多个。 希望这能让您在 Stevens cmets 之后在使用卸载字符串卸载软件方面走上正轨,而不是使用 CIM 方法。

请不要将此标记为答案,我想我会分享一个未完成的有效脚本。

【讨论】:

  • 感谢您的回复。问题是,它不在任何地方的注册表中。我从来没有见过这样的事情。使用 MSI 卸载时出现错误,尝试升级时出现错误。我可以判断这台机器是否已启动程序的唯一方法是查询 Win32_Product。
  • 很可能它已损坏。你是怎么先卸载的?使用产品类?你可以复制一个 MSI 文件并再次运行卸载吗?
  • 我已尝试使用 MSI。它给出了有关未初始化进程的错误。无法升级,在注册表中找不到。事实上,如果我尝试更新客户端,我最终会在 Win32_Product 中得到 2 个条目,这两个条目都不起作用,也都不会删除。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-02-01
  • 2017-06-03
  • 1970-01-01
  • 2022-06-22
  • 1970-01-01
  • 2012-07-27
  • 1970-01-01
相关资源
最近更新 更多