【问题标题】:Uninstall software卸载软件
【发布时间】:2013-10-31 01:22:22
【问题描述】:

我的产品有一个帮助程序可执行文件来卸载所有相关的子产品。我根据所有子产品的升级代码卸载。

首先,我使用MsiEnumRelatedProducts 函数从升级代码中获取产品代码。然后我尝试使用MsiConfigureProductEx函数卸载产品。

问题是MsiConfigureProductEx 正在返回错误。

调用函数:MsiConfigureProductsEx
返回码:1605 (0x00000645)
说明:此操作仅对当前安装的产品有效。

为什么MsiEnumRelatedProducts 返回一个无效的产品代码?我搜索了 Windows 注册表以查看是否存在此类产品代码。没有。如何调试问题?

编辑:添加了重现问题的最少代码。

// UpgradeCodes is an array having upgrade codes of all modules.

TCHAR lpProductCode[GUID_STR_LENGTH];
const TCHAR tszNoReboot[] = _T("REMOVE=ALL REBOOT=ReallySuppress DISABLE_REBOOT_PROMPT=1");

for (size_t i = 0; i < sizeof(UpgradeCodes) / sizeof(UpgradeCodes[0]); i++)
{
   tstring tstrUpgradeCode = UpgradeCodes[i];

   DWORD dwIndex = 0;
   size_t status;

   // for each of the upgrade code, get all the products
   do
   {
       status = MsiEnumRelatedProducts(UpgradeCodes[i], 
                                       0, 
                                       dwIndex, 
                                       lpProductCode);
       if (ERROR_SUCCESS == status)
       {
          UINT uiReturn = MsiConfigureProductEx(lpProductCode, 
                                                INSTALLLEVEL_DEFAULT, 
                                                INSTALLSTATE_DEFAULT, 
                                                tszNoReboot);

          if (ERROR_SUCCESS_REBOOT_REQUIRED == uiReturn)
          {
               // prompt for reboot at the end of all modules uninstallation.
          }

          if (ERROR_SUCCESS != uiReturn)
          {
              // log message with return code.

              // Error Code: 1605 is coming from here.
          }
       }
   }while (ERROR_NO_MORE_ITEMS != status);
}

【问题讨论】:

  • 请出示您的实际代码。您是否在调用MsiConfigureProductEx() 之前检查MsiEnumRelatedProducts() 的返回值是否有错误?你传递给MsiConfigureProductEx()的参数是什么?
  • @RemyLebeau 添加了重现问题的代码。
  • @RemyLebeau 我从添加或删除程序中手动卸载了一个子模块,然后我运行了我的实用工具。从那时起,这个问题就出现了。我检查了 Windows 注册表中的升级代码,以查看失败场景的升级代码是否存在。没有这样的升级代码。因此,MsiEnumRelatedProducts 不应该返回任何产品代码。 Windows 安装程序在某处缓存此信息。
  • The documentation 说“这个函数列出了当前安装的和广告在其属性表中具有指定 UpgradeCode 属性的产品”。所以它确实会返回未安装的产品。尝试致电MsiQueryProductState 以查看 MSI 认为产品处于什么状态。不要卸载任何未安装的产品。
  • 您能否尝试完整阅读此主题(使用 cmets):stackoverflow.com/questions/22583822/…。重大升级失败并注册了两个版本的应用程序并非不可能。我对此表示怀疑,但请务必阅读该主题,因为问题似乎很重要。我稍后再回来查看。

标签: windows winapi windows-installer installation


【解决方案1】:

几年过去了,我想添加两个可以使用的scipt 导出 MSI 包信息: How can I find the product GUID of an installed MSI setup? - 在第 2 节

请访问上面的链接,但这里是脚本的直接链接: 1) the html export version2) the simpler text output


免责声明:以下信息非常“隐秘”。请尽可能使用 API 调用来访问 MSI 数据库。还要记住在虚拟机上运行所有 MSI 测试,以便轻松恢复到“干净状态”。在 MSI 开发过程中,可能会发生奇怪的事情。


您的该产品之前的卸载可能会在卸载时留下一些注册的内容,这会导致所有问题。我会尝试使用脚本检查系统上注册的内容。

在这里找到了关于使用 VBScript 检索产品信息的良好讨论,一些非常好的脚本 - 推荐。去网站查找脚本,它们在这里的格式很差,并堵塞了答案。

Windows Installer 数据库主要位于此处:

  • HKEY_CLASSES_ROOT\Installer\
  • 升级代码部分:HKEY_CLASSES_ROOT\Installer\UpgradeCodes

您绝不能直接接触 Windows Installer 数据库注册表中的任何内容。它非常相互关联且容易损坏。仅通过 API。请注意,注册表中的 GUID 是打包好的,因此您不会从注册表中的包中找到 GUID。

  • 打包的 GUID:03B1692A57845354EA63AD602436AB05
  • 常规 GUID:{A2961B30-4875-4535-AE36-DA064263BA50}

使用上面的 VBScripts 和直接检查注册表数据,您应该能够确定 Windows Installer 数据库中发生了什么。

【讨论】:

    【解决方案2】:

    我永远不会直接在 C++ 中工作来测试这一点。相反,我会通过尝试 PowerShellVBScript 来确定卸载例程的问题,从而消除一些复杂性。您可以在this thread 中找到有关如何使用这些脚本工具的信息。而here 是另一个线程。

    • 尚不清楚某些卸载是否有效,并且有 一个失败或卸载操作完全失败?那是 第一个问题。
    • 您是否尝试过手动卸载添加/删除中的所有产品以确保手动正确卸载它们?其中一种产品可能会在卸载期间触发错误返回代码,该代码以编程方式捕获,但在手动安装期间被忽略。通常这些可以来自放置在 InstallFinalize 之后的自定义操作。在这种情况下,需要重新设计一些设置。在最简单的情况下,它会涉及禁用自定义操作的错误检查,但我认为该修复还不够好。
    • 产品可能是 已安装,但每个用户。换句话说,它可能只安装 对于机器上的单个用户,而不是机器(这由ALLUSERS 属性控制)。如果是这种情况,我不确定此功能如何工作 - 它甚至可能将产品报告为广告(可通过快捷方式按需安装,但未实际安装)。同样,我还没有尝试过,卸载可能仍然有效。我只是想尝试给你一些指示。
    • 您是否在安装产品的过程中对现有 MSI 文件进行了任何重大升级

    还有一个问题:您是否在 Windows 8 上运行?这些 MSI 文件是用 WIX 还是其他工具生成的? problems that appear at least remotely similar 有一些断断续续的报告。

    【讨论】:

    • 如果使用Add/Remove 程序运行,产品已正确卸载。我将查看安装/卸载日志以查看是否有任何警告。我正在使用 Win 8.1。安装程序文件是使用Advanced Installer 构建的。主要问题是MsiEnumRelatedProducts 提供系统上不再存在的产品。所以,问题归结为操作系统在哪里存储相应升级代码的产品代码。
    【解决方案3】:

    如果您有包安装程序(如 Microsoft SQL Server),它可以在安装阶段安装许多其他项目。

    以后你去卸载大包安装程序的时候,理论上应该把安装程序添加到系统中的所有项目都删除了。

    因此,请尝试卸载您的应用程序,停止,然后查看其他较小的应用程序是否仍在系统上。

    如果是,那么您需要在自定义卸载脚本启动时首先卸载这些单独的应用程序。

    我假设你已经有一个 System.Configuration.Install.Installer 类。安装应用程序时遵循一组步骤(1、2、3 等),然后在卸载应用程序时按相反顺序执行这些步骤(3、2、1)。

    【讨论】:

      【解决方案4】:

      为您尝试新方法。我找到了两种产品,它们似乎至少注册了两个产品代码作为升级代码。它们是:MSVC redistributable 2008MSXML 4.0 SP2。我编写了一个小型 C++ 测试,似乎可以正常工作。

      基本上我认为您需要在循环的下一次迭代之前检查 ERROR_NO_MORE_ITEMS,这样您就不会尝试卸载不再安装的产品。

      这里有一些 VS2013 代码,应该在全新安装的空项目上立即编译。


      更新:更新代码以使用 VS2017 和最小的控制台应用程序。

      1. 新建一个控制台项目:File =&gt; New =&gt; Project... =&gt; Visual C++, Windows Desktop, Windows Console Application

      2. 将以下代码粘贴到主 CPP 文件中(替换其中的任何内容)

      3. 设置断点并构建并运行 (F5)

      4. F10 单步执行

      5. 如果未安装“Microsoft Visual C++ 2008 Redistributable”,将找不到相关产品代码。


      #pragma once
      #include "stdafx.h"
      
      // The below should really be in stdafx.h (precompiled header)
      #define WIN32_LEAN_AND_MEAN // Exclude stuff from Windows.h
      #define STRICT
      #include <windows.h>
      #include <msi.h>
      
      #pragma comment(lib, "msi.lib") // To make code link
      
      int main()
      {
          UINT i = 0;
          UINT status = ERROR_SUCCESS;
          TCHAR productcode[39] = {};
      
          const TCHAR upgradecode[39] = L"{AA783A14-A7A3-3D33-95F0-9A351D530011}"; //Microsoft Visual C++ 2008 Redistributable
          //const TCHAR upgradecode[39] = L"{7CE723E3-E56B-432C-9F24-78C0606045A5}"; // MSXML 4.0 SP2 (KB973688)
      
          do
          {
              // look up (related) product code(s) for specified upgrade code
              status = MsiEnumRelatedProducts(upgradecode, 0, i, productcode);
      
              if (status == ERROR_NO_MORE_ITEMS) // Test here. 259, ERROR_NO_MORE_ITEMS
              {
                  // No more productcodes for specified upgrade code
                  MessageBox(NULL, L"No more productcodes", L"Done", MB_OK);
      
                  break;  // exit do-while loop
              }
      
              i++; // Next product code
      
              MessageBox(NULL, productcode, L"Product Code:", MB_OK);
      
          } while (status != ERROR_NO_MORE_ITEMS);
      
          return 0;
      }
      

      由于重大升级失败或类似的高级错误情况,您的系统上可能存在错误注册的产品,所以我不确定这是否能解决您的问题。 p>

      请记住,位于 HKEY_CLASSES_ROOT\Installer\UpgradeCodes 的 Windows Installer 数据库包含打包的 GUID。您可以尝试以下链接中的 VBScript 代码在打包和常规 GUID 格式之间来回转换:http://www.symantec.com/connect/blogs/guid-converter

      更多关于 guid 格式的信息,如果感兴趣的话:http://www.symantec.com/connect/articles/working-darwin-descriptors

      // 2014 年测试数据(不同格式的指南):

      // UpgradeCode
          // 41A387AA3A7A33D3590FA953D1350011 => {AA783A14-A7A3-3D33-95F0-9A351D530011}
          // 
      // ProductCode
          //
          // Microsoft Visual C++ 2008 Redistributable - x86 9.0.30729.4148
          // CFD2C1F142D260E3CB8B271543DA9F98 => {1F1C2DFC-2D24-3E06-BCB8-725134ADF989}
          //
          // Microsoft Visual C++ 2008 Redistributable - x86 9.0.30729.17
          // D20352A90C039D93DBF6126ECE614057 => {9A25302D-30C0-39D9-BD6F-21E6EC160475}
      
      
      // UpgradeCode
          // 3E327EC7B65EC234F942870C0606545A => {7CE723E3-E56B-432C-9F24-78C0606045A5}
          // 
      // ProductCode
          // 
          // MSXML 4.0 SP2 (KB973688)
          // 6E8A266FCD4F2A1409E1C8110F44DBCE => {F662A8E6-F4DC-41A2-901E-8C11F044BDEC}
      
          // MSXML 4.0 SP2 (KB954430)
          // DDA39468D428E8B4DB27C8D5DC5CA217 => {86493ADD-824D-4B8E-BD72-8C5DCDC52A71}
      

      【讨论】:

      • 感谢您的宝贵时间。我会在今天晚些时候尝试并回复您。
      猜你喜欢
      • 1970-01-01
      • 2010-11-19
      • 2019-01-19
      • 2017-10-11
      • 2018-09-18
      • 2013-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多