Lykathia
Lykathia

Reputation: 440

Skip custom action on uninstall (WiX)

I'm authoring a MSI for an existing product. In the previous version there was a custom action that wasn't restricted to only run on install, and now fails on uninstall w/ MajorUpgrade.

Is there any way in the new installer, to tell WiX to skip over that particular custom action on uninstall?

Upvotes: 5

Views: 909

Answers (2)

LudoBar
LudoBar

Reputation: 9

The accepted answer from Bodgan Mitrache does not seem to be completely valid since it's not possible to run msiexec command from another msi package.

The first part is still correct. You have to fix the problem in the wix project and generate a new package with the same ProductCode/Version and with the MSI database only. We will use this package to recache the previous one so that it will be possible to uninstall it successfully.

In the new installer, you need 2 steps:

  1. recaching the previous msi
  2. launch new msi (product update)

The only way I found is to create a Wix bundle to chain sequentially the 2 steps.

1- Recaching operation can be made by a standalone executable consisting in a simple call to msiexec /fv valid_recache.msi. Here is a C++ example:

#include <Windows.h>

int main()
{
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

    SHELLEXECUTEINFO shExecInfo;
    shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
    shExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE;
    shExecInfo.hwnd = NULL;
    shExecInfo.lpVerb = L"open";
    shExecInfo.lpFile = L"cmd";
    shExecInfo.lpParameters = L"/c msiexec /fv path_to\\recache.msi";
    shExecInfo.lpDirectory = NULL;
    shExecInfo.nShow = SW_SHOW;
    shExecInfo.hInstApp = NULL;
    ShellExecuteEx(&shExecInfo);
    WaitForSingleObject(shExecInfo.hProcess, INFINITE);
}

2- Create your new msi from your project as usual (with new ProductCode and upper version number)

Combine the 2 steps in a Wix bundle project which can looks like this (useful link here):

<?xml version="1.0" encoding="UTF-8"?>
<Wix 
  xmlns="http://schemas.microsoft.com/wix/2006/wi"
  xmlns:bal="http://schemas.microsoft.com/wix/BalExtension"
  xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">

  <Bundle 
    Name="your_name" 
    Version="1.0.0" 
    Manufacturer="your_organization" 
    UpgradeCode="your_uuid" 
    Copyright="your_copyright" 
    AboutUrl="your_url">

    <BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.HyperlinkLargeLicense">
      <bal:WixStandardBootstrapperApplication
        LicenseUrl=""
        LogoFile="your_logo"
        ShowVersion="yes"
        SuppressOptionsUI="yes"/>
    </BootstrapperApplicationRef>

    <!--Do registry search to find the current msi package version-->
    <util:RegistrySearch 
      Root="HKLM" 
      Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{ProductCode}"
      Value="Version"
      Variable="CurrentMsiPackageVersion"
      Result="value" 
      Win64="yes" />

    <Chain>
      <!--Do recaching only if the current version is the invalid one-->
      <ExePackage 
          Id="Patch" 
          SourceFile="MsiRecache.exe"
          Vital="yes"
          InstallCondition="(CurrentMsiPackageVersion="invalid_msi_version")">

          <Payload Name="recache.msi" SourceFile="relative_path_to\recache.msi"/>
      </ExePackage>

      <!--Launch new installation-->
      <MsiPackage 
        Id="your_id"
        SourceFile="your_setup.msi"
        DisplayInternalUI="yes"
        Vital="yes"/>
    </Chain>
  </Bundle>
</Wix>

Last, generate the bundle by calling:

candle.exe -ext WixBalExtension -ext WixUtilExtension bundle.wxs

light.exe -ext WixBalExtension -ext WixUtilExtension bundle.wixobj

Note that the bundle will have .exe extension instead of msi.

Upvotes: 0

Bogdan Mitrache
Bogdan Mitrache

Reputation: 11023

Yes, you can do this automatically from the new installer.

The solution:

1) You need to fix the problem in the project that builds your current version of the installer and build the good MSI again, from that project. In this case you only need the MSI database, which normally is a few KB, not the entire setup package (i.e CAB files containing all the installation files, etc...)

2) In the new installer, you need a custom action that runs before RemoveExistingProducts standard action which will recache the MSI for the previous version on the machine. Your custom action must execute this command:

msiexec.exe /fv "< path_to_MSI >"

The MSI you're trying to recache the is the new one you'be built in step #1. You need to include this MSI as a resource in the new version installer (and future versions, in case some users skip this version), and pass to you custom action the full path, where this MSI file is extracted, as a parameter.

Basically what you are doing to is automate the steps of recaching the old installation with a correct MSI (where you have set the correct conditions on your custom action). Now, when the RemoveExistingProducts standard action will execute, Windows Installer will trigger the uninstall of the old version using the newly cached MSI, which has the correct conditions set on your custom action, and uninstall successfully.

Upvotes: 3

Related Questions