如何使用卸载路径卸载 MSI [英] How to uninstall MSIs using the Uninstall Path

查看:32
本文介绍了如何使用卸载路径卸载 MSI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

到目前为止我的代码是.

<预><代码>$app = @(msi1"、msi2"、msi3"、msi4")$Regpath = @('HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*''HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*')foreach($apps 中的 $apps){$UninstallPath = Get-ItemProperty $Regpath |其中 {$_.displayname -like "*$apps*"} |Select-Object -Property UninstallString$UninstallPath.UninstallString#Invoke-Expression UninstallPath.UninstallString#start-process msiexec.exe"-arg "X $UnistallPath/qb";- 等待}

这将返回以下结果:

<代码>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";- 等待 但是这会产生同样的问题.

解决方案

注意:

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

问题:

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

  • 因此,如果您将它们传递给 Invoke-Expression,即如果它们包含没有特殊含义的未加引号字符外部 shell/到 cmd.exe,但在 PowerShell 中是 元字符,这适用于 {}案例.

解决方案:

您有两个选择:

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

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

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

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

(a) 的实施:

# 只需将卸载字符串(命令行)传递给 cmd.exe# 通过`cmd/c`.cmd/c $卸载字符串$exitCode = $LASTEXITCODE

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

(b) 的实施:

# 将命令行拆分为可执行文件和参数列表.# 考虑到可执行文件名称可能被双引号引起来的事实.如果 ($UninstallString[0] -eq '"') {$unused, $exe, $argList = $UninstallString -split '"', 3}别的 {$exe, $argList = $UninstallString -split ' ', 2}# 使用 Start-Process 和 -Wait 等待命令完成.# -PassThru 返回一个代表启动进程的对象,# 然后可以查询其 .ExitCode 属性.$ps = if ($argList) {Start-Process -Wait -PassThru $exe $argList} 别的 {Start-Process -Wait -PassThru $exe}$exitCode = $ps.ExitCode

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


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

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

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

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

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

[1] 如果您不介意额外的开销,您甚至可以(暂时)使用 PackageManagement 模块"https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Windows_PowerShell_Compatibility" rel="nofollow noreferrer">Windows PowerShell 兼容性功能:
Import-Module -UseWindowsPowerShell PackageManagement.

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

I am trying to get the uninstall paths of a set of applications and uninstall them. So far i an get the list of uninstall paths. but i am struggling to actually uninstall the programs.

My code so far is.


    $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
    }

this will return the following results:


    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}

Im struggling to find away to execute these uninstall paths and actually uninstall the MSIs.

I have tried to use Invoke-Expression $UninstallPath.UninstallString but it just displays the windows installer and gives me the option for msiexec.

I have also tried to use start-process "msiexec.exe" -arg "X $UnistallPath /qb" - wait however this gives the same issue.

解决方案

Note:

  • This answer addresses the question as asked.
  • js2010's helpful answer shows a much more convenient alternative that avoids the original problem, via the PackageManagement module's Get-Package and Uninstall-Package cmdlets. Note, however, that these cmdlets only support installed applications in the Windows PowerShell edition - by contrast, PowerShell (Core) as of v7.1 lacks the relevant package providers,[1] and it's unclear (to me) whether they will ever be added.

Problem:

  • The uninstallation command lines stored in the UninstallString / QuietUninstallString registry values[2] are designed for no-shell / from-cmd.exe invocations.

  • They therefore can fail from PowerShell if you pass them to Invoke-Expression, namely if they contain unquoted characters that have no special meaning outside shells / to cmd.exe, but are metacharacters in PowerShell, which applies to { and } in your case.

Solutions:

You have two options:

  • (a) Simply pass the uninstallation string as-is to cmd /c

    • Note that a msiexec-based uninstallation command will then execute asynchronously, because msiexec.exe isn't a console application.
  • (b) Split the uninstallation string into executable and argument list, which allows you to call the command via Start-Process

    • This allows you to add use the -Wait switch to ensure that the installation completes before your script continues.

Note: The following commands assume that the uninstall string is contained in variable $UninstallString (the equivalent of $UninstallPath.UninstallString in your code):

Implementation of (a):

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

$exitCode = $LASTEXITCODE

The automatic $LASTEXITCODE variable can then be queried for the command line's exit code, but note that with asynchronously executing executables such as msiexec this will only tell you whether the executable was launched successfully; to learn the ultimate outcome, you need synchronous execution - see below.

Implementation of (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

You could also add -NoNewWindow to prevent console program-based uninstallation command lines from running in a new console window, but note that the only way to capture their stdout / stderr output via Start-Process is to redirect them to files, using the -RedirectStandardOutput / -RedirectStandardError parameters.


Edition-specific / future improvements:

The Start-Process-based method is cumbersome for two reasons:

  • You cannot pass whole command lines and must instead specify the executable and arguments separately.

  • In Windows PowerShell (whose latest and final version is 5.1) you cannot pass an empty string or array to the (positionally implied) -ArgumentList parameter (hence the need for two separate calls above).

    • This problem has been fixed in the cross-platform, install-on-demand PowerShell (Core) edition (versions 6 and above).

[1] If you don't mind the extra overhead, you can (temporarily) import the Windows PowerShell PackageManagement module even from PowerShell (Core), using the Windows PowerShell compatibility feature:
Import-Module -UseWindowsPowerShell PackageManagement.

[2] As shown in your question, they are stored in the HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall (64-bit applications) and HKEY_LOCAL_MACHINE\Wow6432Node \Software\Microsoft\Windows\CurrentVersion\Uninstall (32-bit applications) registry keys.

这篇关于如何使用卸载路径卸载 MSI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆