Search code examples
wix

Detect re-installation on rollback


This is related to the this question.

As recap:
I have a WiX 4 bundle that installs multiple MSIs (also authored using WiX 4), lets name them MsiA, MsiB and MsiC. All components are under my control. The updates in all MSIs are authored as MajorUpgrade with Schedule="afterInstallInitialize".

  • MsiA installs a Windows service (MsiAService).
  • MsiB also installs a Windows service (MsiBService) which depends on MsiAService.
  • MsiC doesn't contain a service.

This order is also reflected in the Chain element of my bundle. All services are configured using ServiceInstall, ServiceDependency and ServiceControl elements.

Now, if I upgrade the bundle, and MsiB fails, the rollback takes place, which is desired. But in this process, the following happens:

  • MsiB is rolled back and starts it's MsiBService.
  • The bundle of the previous version is started and re-installs MsiA: It stops it's MsiAService and since MsiBService dependends on it, it also gets shut down.
  • After it's rollback, it starts its MsiAService.
  • -> issue: the dependent service MsiBService stays stopped.

Since there is apparently no built in way to start the MsiBService in this case (see question above), I wrote my own custom action that does this in MsiA.

Remaining problem: the conditional scheduling.

How do I detect (in the bootstrapper or in MsiA), that I'm running "the re-installation started from the previous bundle in the case of the rollback"?

Currently I have...

<Custom Action="Tools.ExecuteServiceCommand"
        After="StartServices"
        Condition="WIX_UPGRADE_DETECTED" />

...but this condition does obviously not work.


Solution

  • Since nobody had an idea, I came up with the following condition in the bootstrapper:

    var engineNumericVariables = bootstrapper.Engine.NumericVariables;
                
    engineNumericVariables["RelatedBundleRollbackReinstallCalled"] =
                        saintBootstrapper.LaunchAction == LaunchAction.UpdateReplace &&
                        bootstrapper.Command.Action == LaunchAction.Install &&
                        bootstrapper.Command.Relation == RelationType.Upgrade
                            ? 1
                            : 0;
    

    The value of this engine variable is passed to the affected MSI and evaluated there.

    <Custom Action="Tools.ExecuteServiceCommand"
            After="Tools.ExecuteServiceCommand.SetData"
            Condition="(NOT Installed) AND (NOT REMOVE) AND (RELATEDBUNDLEROLLBACKREINSTALLCALLED = 1)" />
    

    For the custom action implementation used, see the linked question.

    If someone has a better idea or sees an error on it, feel free to comment on it.