Uninstall software

2019-06-17 06:07发布

问题:

My product has a helper executable to uninstall all related sub-products. I uninstall based on upgrade codes of all sub-products.

First, I fetch the product code from upgrade code using MsiEnumRelatedProducts function. Then I try to uninstall the product using MsiConfigureProductEx function.

The problem is MsiConfigureProductEx is returning error.

Invoked Function: MsiConfigureProductsEx
Return Code: 1605 (0x00000645)
Description: This action is only valid for products that are currently installed.

Why is MsiEnumRelatedProducts returning a invalid product code ? I searched through the windows registry to see if such product code exists. There isn't any. How to debug the issue ?

Edit: Added minimum code that reproduces issue.

// 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);
}

回答1:

It is possible that a previous uninstall of that product of yours left something registered upon uninstall, and this is causing all the problems. I would try to check with scripts what is registered on the system.

Found good discussions of retrieving product info with VBScript here, a couple of really good scripts - recommended. Go to the sites to find the scripts, they format pretty poorly here and clog the answer.

  • http://forum.installsite.net/index.php?act=ST&f=26&t=14035
  • http://www.dwarfsoft.com/blog/2010/06/22/msi-package-code-fun/

The Windows Installer database is mostly located here:

  • HKEY_CLASSES_ROOT\Installer\
  • The upgrade code section: HKEY_CLASSES_ROOT\Installer\UpgradeCodes

You must never touch anything in the Windows Installer Database Registry directly. It's extremely interconnected and easy to corrupt. Only go through the APIs. Note that the GUIDs in the registry are packed, so you won't find the GUIDs from the package in the registry.

  • Packed GUID: 03B1692A57845354EA63AD602436AB05
  • Regular GUID: {A2961B30-4875-4535-AE36-DA064263BA50}

Using the VBScripts above and the registry data directly for inspection you should be able to determine what is happening in the Windows Installer database.



回答2:

I would never work straight in C++ to test this out. Instead I would eliminate some complexity by trying PowerShell or VBScript to determine what is wrong with the uninstall routine. You can find information on how to use these scripting tools here in this thread. And here is another thread.

  • It is not quite clear if some of the uninstalls work, and there is one that fails or if the uninstall operation fails altogether? That's the first question.
  • Have you tried manually uninstalling all products from add/remove to ensure they all uninstall correctly manually? One of the products could trigger an error return code during uninstall that is caught programatically, but is ignored during manual install. Often these can be from Custom Actions placed after InstallFinalize. In this case some setup redesign is called for. In the simplest case it would involve disabling error checking for the custom action, but that fix is not good enough in my opinion.
  • It is possible that the product is installed, but per-user. In other words it might be installed only for a single user on the machine, and not for the machine (this is controlled by the ALLUSERS property). I am not sure how this function works if this is the case - it may even report the product as advertised (available for on demand install via a shortcut, but not actually installed). Again, I have not tried this, and uninstall may still work. Just off the top of my head to try and give you some pointers.
  • Have you performed any major upgrades of existing MSI files as part of the install of your product?

One further question: are you running on Windows 8? And are these MSI files generated with WIX or some other tool? There have been some intermittent reports on problems that appear at least remotely similar.



回答3:

If you have a package installer (like Microsoft SQL Server), it can install a host of other items during its installation phase.

Later, when you go to uninstall the big package installer, all of the items that installer added to the system should theoretically be removed.

So, try just uninstalling your application, stop, then look to see if the other smaller applications are still on the system.

If they are, then you will need to uninstall these individual applications first when your custom uninstall script starts.

I assume you already have a System.Configuration.Install.Installer class. Follow a set of steps when installing your application (1, 2, 3, etc.), then perform these steps in reverse order when uninstalling your application (3, 2, 1).



回答4:

Trying a new approach for you. I have located two products that seem to have at least two productcodes registered for their upgrade codes. They are: MSVC redistributable 2008 and MSXML 4.0 SP2. I have written a small C++ test that seems to work ok.

Essentially I think you need to check for ERROR_NO_MORE_ITEMS before the next iteration of the loop so you don't try to uninstall products that are no longer installed.

Here is some VS2013 code that should compile out of the box on a fresh install, empty project.


UPDATE: updated code to use VS2017 and a minimal console application.

  1. Create a new console project: File => New => Project... => Visual C++, Windows Desktop, Windows Console Application

  2. Paste the below code into the main CPP file (replacing whatever is there)

  3. Set a breakpoint and build & run (F5)

  4. F10 to step through

  5. If "Microsoft Visual C++ 2008 Redistributable" isn't installed, no related product codes will be found.


#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;
}

There could be erronously registered products on your system due to failed major upgrades or similar advanced error scenarios, so I am not sure if this solves your problem.

Keep in mind that the Windows Installer Database at HKEY_CLASSES_ROOT\Installer\UpgradeCodes contains packed GUIDs. You can try the VBScript code found in the following link to convert back and forth between packed and regular GUID formats: http://www.symantec.com/connect/blogs/guid-converter

More info on guid formats here if it is interesting: http://www.symantec.com/connect/articles/working-darwin-descriptors

// TEST DATA 2014 (guids in different formats):

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