【问题标题】:Is there an alternative to GUID when using msiexec to uninstall an application?使用 msiexec 卸载应用程序时是否有 GUID 的替代方法?
【发布时间】:2018-07-18 01:32:27
【问题描述】:

在运行包含 msiexec 的卸载脚本时,我们目前正在使用 GUID 来识别应用程序。我遇到的问题是每次安装最新版本的应用程序时 GUID 都会发生变化,所以我想知道是否有不同的方法可以识别使用 msiexec 运行的应用程序?

【问题讨论】:

  • 你不能选择卸载选项来定位 msi 吗?
  • 您指的是哪个 GUID?
  • 我没有适合您的 Powershell 脚本,但我在下面的 VBScript 中添加了一个粗略的演示,展示了一种方法。 PhilDW 描述了另一种方法。我在下面的回答中添加了一个链接 - 指向底部 - 指向一个小型 VBScript,它将带有产品代码和产品名称的已安装产品列表导出到您的桌面。

标签: powershell installation uninstallation windows-installer


【解决方案1】:

您知道什么不会改变,或者确实会改变但您可以轻松跟踪?

  • 说产品代码发生了变化,但升级代码没有。您可以按照 PhilDW 的建议,使用 MsiEnumRelatedProductsInstaller.Related 或等效项从升级代码中检索产品代码。

  • 说名字不一样。您可以按照 Stein 的建议按产品名称查找产品代码,以 MsiEnumProductsExInstaller.ProductsEx 或同等名称开头。

  • 假设您将安装 .msi 的本地副本缓存在一个不变的位置,或者您的脚本很容易找到的位置。您可以按照 4c74356b41 的建议在命令行中将该路径用于 msiexec。 (请注意,msiexec /x 接受包路径或产品代码。)

  • 说你不喜欢这些选项中的任何一个。也许您可以在 HKEY_LOCAL_MACHINE\Software\YourCompany\YourProduct 中添加一个注册表值,将字符串值 ProductCode 设置为[ProductCode]。然后您可以使用reg query HKLM\Software\YourCompany\YourProduct /v ProductCode 或等效项(抱歉,我是 PowerShell 的笨蛋)来获取当前产品代码。 (当然,至少在考虑 32 位和 64 位路径的情况下。)

【讨论】:

    【解决方案2】:

    首先:新版本的应用程序的产品GUID发生变化是正常的,但也可以在不改变产品GUID的情况下升级某些应用程序(简称minor upgrades - 与更改产品 GUID 的 major upgrades 相反)。 UpgradeCode(它定义了一系列相关产品)往往在同一产品的不同版本之间保持稳定。 ProductCode 唯一标识产品(在特定版本中)。

    这里列出了一些卸载选项:Uninstalling an MSI file from the command line without using msiexec

    我想您可以使用section 3 中所示的 MSI 文件名,或者如果产品名称保持稳定,您可以使用它以及自动化来查找正确的产品 G​​UID 以卸载有问题的产品。我稍后会对此进行测试并更新答案。


    更新:通过“产品名称”卸载产品的示例 VBScript(假设它在不同版本中保持不变,通常会这样做,但不能保证 - 它取决于产品)。

    在“添加/删除程序”中查找产品名称 - 或使用链接到此答案底部的小 VBScript 导出一个包含所有已安装包信息的小文本文件。

    ' On Error Resume Next ' Used to suppress errors
    
    Const msiUILevelNone = 2
    Const msiUILevelFull = 5
    Const msiInstallStateAbsent = 2
    
    Set installer = CreateObject("WindowsInstaller.Installer")
    Set products = installer.ProductsEx("", "", 7)
    installer.UILevel = msiUILevelFull  ' Running with full GUI (if available in MSI)
    ' installer.UILevel = msiUILevelNone ' Will run uninstall silently, run script with admin rights
    
    ' Get the product name from the user
    productname = InputBox("Please enter the product name for the MSI package you wish to uninstall:")
    If productname = vbCancel Or Trim(productname) = "" Then
       WScript.Quit(0)
    End If    
    
    ' Iterate over all MSI packages on the box
    For Each product In products
    
       currentproduct = product.InstallProperty("ProductName")
    
       If LCase(currentproduct) = LCase(productname) Then
    
          installer.ConfigureProduct product.productcode, 0, 2 ' msiInstallStateAbsent
          MsgBox "Ran uninstall for: " & currentproduct
          Exit For ' End product iteration, assuming only one product needed uninstall
    
       End If 
    
    Next
    
    Set installer = Nothing
    
    MsgBox "Finished."
    

    更新:您可以使用 VBScript 为自己创建一个产品代码和产品名称的快速列表,如本答案底部所述:How can I find the product GUID of an installed MSI setup?。我认为这个特殊的 VBScript 尽可能简单。

    【讨论】:

    • 我没有时间阅读所有这些,但我在那篇文章中看到的每个答案实际上都是在使用 msiexec。你只是没有自己调用它。
    • 事实上,不同的选项使用 WMI、COM 自动化、msiexec.exe、Win32 API 函数、Powershell / WMI、.NET 和一些手动选项从 GUI 或第三方启动这些基本技术工具。我猜即使msiexec.exe 在后台使用the C++ Win32 installer functions - 所以这些才是真正的McCoy。其他一切都只是我相信的那些之上的层次。
    • 我曾经使用过所有这些。观察过程。他们都启动 msiexec.exe
    • 是的,在执行 MSI 操作时,通常会产生几个 msiexec.exe 进程——即使使用最先进的 Win32 C++ 函数(刚刚验证)也是如此。一种用于具有用户凭据的 GUI 序列,一种用于具有系统凭据的提升序列,以及用于运行 MSI 引擎所需的自定义操作和其他组件的任意数量的额外 msiexec.exe 进程。一旦它是 MSI,您需要 Windows Installer 引擎来处理它,但它可以通过多种方式调用。我们能否询问您遇到问题的场景的具体细节?
    【解决方案3】:

    在这种情况下,人们绕过各种 ProductCode 值的常用方法是从更恒定的 UpgradeCode 开始。

    鉴于 UpgradeCode,您可以使用 MsiEnumRelatedProducts(或脚本或 MSI 互操作等效项)返回 ProductCode。像这样的代码通常不需要更改。

    我很确定 PowerShell 应该能够做到这一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-22
      • 1970-01-01
      • 2012-05-02
      • 2015-04-28
      相关资源
      最近更新 更多