微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用卸载路径卸载 MSI

如何解决如何使用卸载路径卸载 MSI

我正在尝试获取一组应用程序的卸载路径并卸载它们。到目前为止,我得到了卸载路径的列表。但我正在努力卸载这些程序。

到目前为止我的代码是。


    $app = @("msi1","msi2","msi3","msi4")
     $Regpath = @(
                    'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
                    'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
                )
                   
    foreach ($apps in $app){
    $UninstallPath = Get-ItemProperty $Regpath | where {$_.displayname -like "*$apps*"} | Select-Object -Property UninstallString
    
    $UninstallPath.UninstallString
    #Invoke-Expression UninstallPath.UninstallString
    #start-process "msiexec.exe" -arg "X $UnistallPath /qb" - wait
    }

这将返回以下结果:


    MsiExec.exe /X{F17025FB-0401-47C9-9E34-267FBC619AAE}
    MsiExec.exe /X{20DC0ED0-EA01-44AB-A922-BD9932AC5F2C}
    MsiExec.exe /X{29376A2B-2D9A-43DB-A28D-EF5C02722AD9}
    MsiExec.exe /X{18C9B6D0-DCDC-44D8-9294-0ED24B080F0C}

我正在努力寻找执行这些卸载路径并实际卸载 MSI 的方法

我曾尝试使用 Invoke-Expression $UninstallPath.UninstallString,但它只显示 Windows 安装程序并为我提供了 msiexec 选项。

我也尝试使用 start-process "msiexec.exe" -arg "X $UnistallPath /qb" - wait 但这会产生同样的问题。

解决方法

注意:

  • 这个答案解决了所提出的问题。
  • js2010's helpful answer 展示了一个更方便的替代方案,通过 PackageManagement 模块的 Get-PackageUninstall-Package cmdlet 避免了原始问题。但请注意,这些 cmdlet 仅支持 Windows PowerShell 版本中已安装的应用程序 - 相比之下,PowerShell (Core) 自 v7.1 起缺少相关的包提供者,[1],而且(对我而言)他们是否会被添加还不清楚。

问题:

  • 存储在 UninstallString / QuietUninstallString 注册表值中的卸载命令行[2]专为no-shell而设计em> / from-cmd.exe 调用

  • 因此,如果您将它们传递给 Invoke-Expression,即它们包含未加引号的字符,它们可以失败从 PowerShell在 shell / 到 cmd.exe 之外没有特殊含义,但在 PowerShell 中是 元字符,在您的情况下适用于 {}

解决方案

您有两个选择:

  • (a) 只需将卸载字符串按原样传递给 cmd /c

    • 请注意,基于 msiexec 的卸载命令将异步执行,因为 msiexec.exe 不是控制台应用程序。
  • (b) 将卸载字符串拆分为可执行文件和参数列表,这样您就可以通过Start-Process

    调用命令
    • 这允许您添加使用 -Wait 开关以确保安装在脚本继续之前完成。

注意:以下命令假定卸载字符串包含在变量 $UninstallString(相当于代码中的 $UninstallPath.UninstallString)中:

(a) 的实施:

# Simply pass the uninstallation string (command line) to cmd.exe
# via `cmd /c`.
cmd /c $UninstallString

$exitCode = $LASTEXITCODE

然后可以查询 automatic $LASTEXITCODE variable 以获取命令行的退出代码,但请注意,如果 异步 执行诸如 msiexec 之类的可执行文件,这将只告诉你可执行文件是否启动成功;要了解最终结果,您需要同步执行 - 见下文。

(b) 的实施:

# Split the command line into executable and argument list.
# Account for the fact that the executable name may be double-quoted.
if ($UninstallString[0] -eq '"') {
    $unused,$exe,$argList = $UninstallString -split '"',3
}
else {
    $exe,$argList = $UninstallString -split ' ',2
}

# Use Start-Process with -Wait to wait for the command to finish.
# -PassThru returns an object representing the process launched,# whose .ExitCode property can then be queried.
$ps = if ($argList) {
        Start-Process -Wait -PassThru $exe $argList
      } else {
        Start-Process -Wait -PassThru $exe 
      }
$exitCode = $ps.ExitCode

您还可以添加 -NoNewWindow 以防止 console 基于程序的卸载命令行在新的控制台窗口中运行,但请注意,通过以下方式捕获其 stdout / stderr 输出的唯一方法Start-Process 是使用 -RedirectStandardOutput / -RedirectStandardError 参数将它们重定向到 files


特定于版本/未来的改进:

基于 Start-Process 的方法很麻烦,原因有两个:

  • 您不能传递整个命令行,而必须分别指定可执行文件和参数。

  • Windows PowerShell(最新和 最终 版本为 5.1)中,您不能将 字符串或数组传递给 (位置隐含)-ArgumentList 参数(因此需要上述两个单独的调用)。

    • 此问题已在跨平台、按需安装的 PowerShell (Core) 版本(版本 6 及更高版本)中得到修复。

[1] 如果您不介意额外的开销,您甚至可以(临时)使用 Windows PowerShell compatibility feature 从 PowerShell(核心)导入 Windows PowerShell PackageManagement 模块: Import-Module -UseWindowsPowerShell PackageManagement

[2] 如您的问题所示,它们存储在 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall(64 位应用程序)和 HKEY_LOCAL_MACHINE\Wow6432Node \Software\Microsoft\Windows\CurrentVersion\Uninstall(32 位应用程序)注册表项中。

,

或者例如:

get-package *chrome* | uninstall-package

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。