0
votes

I have WiX bundle with two msi packages: A and B. At first, I successfully installed bundle version 1.0.0.0. Then I am installing MajorUpgrade version 2.0.0.0. Package A successfully upgraded. Package B upgrade fails and rollback started.

I defined msi package upgrade as: <MajorUpgrade AllowSameVersionUpgrades="yes" Schedule="afterInstallInitialize" DowngradeErrorMessage="A newer version of [ProductName] is already installed." />

Package B reverted to version 1.0.0.0. Package A roll backed by removing. So, the bundle remains in inconsistent state.

I need to revert entire bundle to version 1.0.0.0 if update fails. Is it possible?

1
I found discussions for the same issue: here and here It requires Multi-MSI Transactions that currently is not supported by WiX. - Evgeni Lipatov

1 Answers

2
votes

There is no standard way to accomplish it, because multi-MSI transactions are not supported by WiX.

I found a workaround that works for me. I use Custom Bootstrapper Application, so I can handle failure event in C# code. If you use WiX Standard Bootstrapper Application (WiXStdBA) it will not help you.

If update failed, I call previous bundle installer from Windows Package Cache in silent repair mode. It restores previous state.

Next code expresses the idea:

Bootstrapper.PlanRelatedBundle += (o, e) => { PreviousBundleId = e.BundleId; };

Bootstrapper.ApplyComplete += OnApplyComplete;

private void OnApplyComplete(object sender, ApplyCompleteEventArgs e)
{
    bool updateFailed = e.Status != 0 && _model.InstallationMode == InstallationMode.Update;
    if (updateFailed)
    {
        var registryKey = string.Format("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{0}", VersionManager.PreviousBundleId);
        RegistryKey key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(registryKey)
            ?? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey(registryKey);

        if (key != null)
        {
            string path = key.GetValue("BundleCachePath").ToString();
            var proc = new Process();
            proc.StartInfo.FileName = path;
            proc.StartInfo.Arguments = "-silent -repair";
            proc.Start();
            proc.WaitForExit();
        }
    }
}