使用 msiexec 卸载应用程序时是否有 GUID 的替代方法?

Is there an alternative to GUID when using msiexec to uninstall an application?

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

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

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

我想您可以使用 section 3 中所示的 MSI 文件名,或者如果产品名称保持稳定,您可以将其与自动化一起使用以查找正确的产品 G​​UID 以卸载有问题的产品.我稍后会测试这个并更新答案。


UPDATE:"product name" 卸载产品的示例 VBScript(假设它在各个版本中保持不变,通常情况下是这样,但不能保证 - 这取决于在产品上)。

在 Add/Remove 程序中找到产品名称 - 或使用链接到此答案底部的微型 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 自己创建一个产品代码和产品名称的快速列表,如本答案底部所述:。我认为这个特定的 VBScript 非常简单。

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

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

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

您知道哪些内容不会改变,或者确实会改变但您可以轻松跟踪?

  • 说产品代码变了,升级代码没变。您可以按照 PhilDW 的建议使用 MsiEnumRelatedProducts or Installer.Related 或等价物从该升级代码中检索产品代码。

  • 说名字不一样了。您可以按照 Stein 的建议按产品名称查找产品代码,以 MsiEnumProductsEx or Installer.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 位路径。)