Lazy Patching in WiX 3.6

by bryanpjohnston

With the upcoming release of WiX 3.6, I like to share about how I have used Burn to implement patching using a process I like to call lazy patching. Now I’ve never been a big fan of creating patches. I prefer to implement major upgrades and change the version number and product code with every build.  That said, I have a requirement to be able to install a patch and have only a few files get modified. Since I am versioning each dll with every build, I can’t use traditional patching techniques of comparing two .msis because each binary would get modified (even if by only a few bits).

Thanks to Burn, we can easily create a Bundle that includes all of our prereqs and packages to be included in a single product deployment. In my WiX Bootstrapper project, I include a RelatedBundle element and specify the UpgradeCode as its Id. So version 1.0.0.0 of MyProduct will have the following bits in Bundle.wxs:

<!– Specify upgrade code in RelatedBundle element so bootstrapper engine
detects if a related bundle is installed. –>
<RelatedBundle Id=PUT-UPGRADECODE-GUID-HERE” Action=Upgrade/>
<
Chain>
  <MsiPackage SourceFile=MyProduct.msi‘ Id=MyProduct‘ Cache=yes‘ Visible=no/>
</Chain>

I have a custom MBA (managed bootstrapper application) that provides a consistent UI and some additional setup logic. In my MBA, I add an event handler to tell the installer what to do when it detects a related bundle. The important part here is when the related operation is a MajorUpgrade or MinorUpgrade (or anything but Downgrade, Repair or Remove), I tell my installer to Install the product.

private void OnDetectRelatedBundle(object sender, DetectRelatedBundleEventArgs e)
{
   switch (e.Operation)
   {
     case RelatedOperation.Downgrade:
       this.DetectedScenario = Scenario.Downgrade;
       break;
     case RelatedOperation.Repair:
       this.DetectedScenario = Scenario.Maintenance;
       break;
     case RelatedOperation.Remove:
       this.DetectedScenario = Scenario.Maintenance;
       break;
     default:
       this.DetectedScenario = Scenario.Install;
       break;
    }
}

Now when it is time to release a patch (1.0.1.0 ) to my product, I just create a second msi (ex: Patch.msi) and add it to the chain of packages to be installed. Then update the version number of the bootstrapper project (Bundle.wxs) and we are done:

<!– Specify upgrade code in RelatedBundle element so bootstrapper engine
detects if a related bundle is installed. –>
<RelatedBundle Id=PUT-UPGRADECODE-GUID-HERE” Action=Upgrade/>
<Chain>
  <!– original 1.0.0.0 msi –>
  <MsiPackage SourceFile=MyProduct.msi‘ Id=MyProduct‘ Cache=yes‘ Visible=no/>
  <!– patch 1.0.1.0 msi –>
  <MsiPackage SourceFile=Patch.msi‘ Id=Patch1‘ Cache=yes‘ Visible=no/>
</Chain>

Now when you install v1.0.0.0, it will install MyProduct.msi. When you install v1.0.1.0, it will detect MyProduct.msi is installed, and then install Patch.msi over top. If MyProduct.msi is not installed, then it will install both msis.

Advertisements