Burn: Zero, one, or n

It’s a joke that developers only know about three quantities: zero, one, or n. In other words, once you write the code to handle two, you’ve already written the code to handle millions.

That applies to WiX’s Burn chainer engine as well: Burn is designed to handle any number of packages, all of which could be installed in a chain. That design differs from one many are familiar with: The fire-and-forget bootstrapper that installs some number of prerequisites before kicking off “the real installer.” Three important words there are “the,” “real,” and “installer.”

“The” real installer

As a rule, bootstrappers installing their prerequisites and then launch “the” .msi package in full UI mode. That works in a lot of cases, but what happens when there isn’t a “the” package but two or three or more? You don’t need a huge product to take advantage of multiple .msi packages. For example, language packs — separate .msi packages with the localizable resources for a particular language — are a great way of handling localization. But you’re guaranteed to have multiple .msi packages, one for your language-neutral resources and one for localized resources.

Supporting only a single main .msi package was a non-starter for Burn. It’s far too limiting in this day and age.

Obviously, a phased bootstrapper could also support multiple non-prerequisite packages. I’m not aware of any that do.

The “real” installer

Fire-and-forget bootstrappers force users to be attentive to the installation over multiple phases. Users rarely care about the anally-retentive distinction between a program and its prerequisites. They just know that installing a program using a bootstrapper requires them to sit at their machine while all the prerequisites are installed before they get to go through another (subtly different) user interface to install the “real” software.

Burn rejects the phased model of installation: All packages, prerequisites or “real,” are installed as part of a single chain of packages. User interaction happens before any packages are installed (modulo progress bars of questionable reliability, error messages, or happy shiny success messages).

The real “installer”

The “forget” part of fire-and-forget is important: Without leaving a “trace” on the system, fire-and-forget bootstrappers don’t involve themselves with maintenance operations like uninstall and repair.

Burn registers an entry in ARP (aka Programs & Features) so that uninstall affects the whole chain, prerequisite and multiple packages alike. You might not want to remove bigger prerequisites like the .NET Framework but leaving behind other prerequisites is kinda rude — at best; prerequisites that aren’t shared shouldn’t be left behind at all.

Repair is sometimes forgotten (intentionally or otherwise). Burn supports repair across the whole chain and makes it optional for packages like the .NET Framework.

If Burn’s so powerful, just make it an option

When we were designing Burn, we knew all about existing bootstrappers but rejected the fire-and-forget, single-package model. It was too limiting in 2008 and it’s even more limiting now. Obviously, however, lots of folks don’t need the firepower of this fully armed and operational battle chainer. That said, there’s nothing but time preventing you from going all-in with the Burn model

Burn partly supports the phased install model. You can set the MsiPackage/@DisplayInternalUI attribute to yes to instruct Burn that when it installs that package, its UI should be shown. @DisplayInternalUI is effective only during install, however; during repair and uninstall, only the bootstrapper application is shown.

There is currently no option to support the “forget” model: Burn always writes an ARP entry for the bundle. Because Burn caches packages by default, it’s important that users have an entry point to uninstall the bundle, which also removes the cached packages and persisted bundle data.

It would be a significant amount of work to mold Burn into something that supports both models. And it would be a mistake. “Just make it an option” is one of the most common ways to create software that is unnecessarily complex and crufty. All code paths end up taxed with the extra complexity.

Instead, imagine a new bootstrapper core — let’s call it Fire, shall we? — built on top of existing Burn services like the planning, download, and install engines. It’s likely less work that throwing in a bunch of switches to the existing Burn engine.

It’s not a sin for a piece of software to not be everything to everybody. There are alternatives, including a bunch of existing bootstrappers. Building a new Fire engine (see what I did there?) would be an interesting bit of work. I like the Burn engine, so I’m not planning on doing it. But you could.

Getting shelf space in the Store

Reading the Windows Store for developers blog recently, I was pleasantly surprised to see that desktop (i.e., non-Metro-style) apps would be allowed to show up in the Windows Store. Desktop apps won’t get the same treatment as Metro-style apps, of course; instead of being able to install apps right from the Windows Store, desktop apps will have a similar listing page with a link to the developer’s site to handle download.

Still, it’s better than nothing — the Windows Store is likely to have a lot of window shoppers looking to buy apps.

However, a hurdle to get an app listing page is that the app must pass “certification.” In the past, this was known as the Windows Logo program and had many pages of technical requirements. In Windows 8, the list is much smaller and less prescriptive. For example, the Logo program for previous versions required the use of MSI, then required MSI or ClickOnce; for Windows 8, there are no technology restrictions.

Some requirements that struck me as interesting:

1.2 Your app must not take a dependency on the VB6 runtime

VB6, IE6 — anything v6 must go away. ๐Ÿ™‚

5.1 Your app must properly implement a clean, reversible installation

If the installation fails, the app should be able to roll it back and restore the machine to its previous state.

If Windows 8 certification is anything like previous versions’, it will involve automated test suites that will highlight rollback that’s less than perfect.

5.4 Your app must never block silent install/uninstall

So don’t throw UI from custom actions and don’t rely on being able to prompt the user (i.e., also don’t break requirement #5.1).

6.1 All executable files (.exe, .dll, .ocx, .sys, .cpl, .drv, .scr) must be signed with an Authenticode certificate

MSI packages aren’t mentioned. But if you ship a Burn bundle or other bootstrapper .exe, that’s going to require signing.

10.1 Your app must be installed in the Program Files folder by default

There’s nothing mentioned that would exempt per-user apps in general from this requirement, though you can always request exceptions. Given the emphasis Metro places on per-user, restricted apps, perhaps this requirement will be extended/relaxed for per-user desktop apps too.

10.6 Your app must write user data at first run and not during the installation in โ€œper-machineโ€ installations

Long-time readers of wix-users will recognize this as advice given again and again. Good to see Windows catching on. ๐Ÿ™‚

10.7 Exceptions and Waivers

A waiver is required for apps that write to the global assembly cache (GAC) .NET apps should keep assembly dependencies private, and store it in the app directory unless sharing an assembly is explicitly required.

I suspect it’s a response to the GAC getting used too often when it’s not needed or very useful. (See also Rico Mariani’s blog post on using NGen, which falls into the same boat.) Still, I’m mildly surprised to see it a requirement.

12.5 App running under the WoW64 emulator should not attempt to subvert or bypass Wow64 virtualization mechanisms

This one’s interesting, depending how struct “subvert” turns out to be. For example, several WiX custom actions make fairly trivial “subversions” of WoW64 to avoid the need for both 32-bit and 64-bit custom actions just to write to the right version of Program Files.

All in all, there’s nothing terribly surprising in this set of requirements. A lot of them just codify what’s already known as best practice for Windows apps in the age of UAC. Of course, this is just the first version of the certification requirements and Windows 8 isn’t yet shipping; there’s plenty of time for Microsoft to add, drop, or change requirements. In the end, you get to decide whether the effort of certification is worth having a presence in the Windows Store. Remember the Metro-style app folks, who have no choice in the matter: The Windows Store is the only way they can ship.

Paying for upgrades

No, this isn’t a post on the costs of proprietary software but an amplification/clarification to my previous post. On wix-users, there’s a thread on the pains of automating upgrades.

If your product consists of a large number of files and the file set changes regularly โ€“ files being added and removed during the product lifetime โ€“ it’s only natural to want to avoid the cost of hand-authoring setup changes to match. (A little over a year ago, I shipped a product with over 7000 files, so I’m familiar with the pain and desire for automation.)

Unfortunately, our frenemy the component rules makes such automation difficult. The closest we-the-WiX-team has gotten to a complete solution is to create components with one file each, which makes the component eligible to have its component GUID automatically generated (using an asterisk in the Component element’s Guid attribute value).

What doesn’t work is when files are removed. Windows Installer doesn’t let you remove components in a minor upgrade, so using one file per component doesn’t immediately solve the automation problem: Your automatically-generated minor upgrade will be missing a component, which is a mortal component-rule sin.

Nor can you avoid the problem by using multiple files per component because component rules say that components must be immutable: You can’t add or remove resources from a component.

So automating the creation of minor upgrades has additional costs:

  • Additional tooling to try to support removing files without blatantly violating component rules.
  • Additional coding in your product to tolerate “obsolete” files without being able to remove them.

For many types of apps, but especially Web apps with many content files, that’s a huge cost. Being able to ship minor-upgrade patches from an automated build might be a benefit worth the cost. It’s really a decision your team needs to make.

I’ll add that if you don’t anticipate shipping patches on a regular schedule, you might just automate the authoring of your RTM product and pay the price of manually tweaking your setup authoring when you need to ship a patch. Again, it’s a cost-benefit decision your team needs to think about.

Cheap and easy

If you don’t absolutely need to ship patches, you can avoid the costs of minor upgrades by simply using major upgrades. You can remove files without worrying about component-rule violations if you use an “early” scheduling of the RemoveExistingProducts standard action โ€“ before or immediately after the InstallInitialize action.

The MSI SDK notes that scheduling RemoveExistingProducts early is “inefficient” because the files that are same between the two product versions are removed and then reinstalled. But that inefficiency is what lets you remove files and components. If you schedule RemoveExistingProducts immediately before or after InstallFinalize, MSI implements the major upgrade by installing the new version of the product “on top of” the previous version, upgrading files with newer versions, then removing the previous version. MSI increments the reference count of components in both packages during the installation of the newer version, then decrements it during the removal of the previous version. Component reference counting works only if component rules are followed, so it’s pretty much the same as minor upgrades.

If you have a large product, the size and install-time benefits of minor upgrade patches might be worth the development effort. Otherwise, major upgrades scheduled early are a great solution. Your call.

Neither more nor less

‘When I use a word,’ Humpty Dumpty said, in a rather scornful tone,’ it means just what I choose it to mean, neither more nor less.’

‘The question is,’ said Alice, ‘whether you can make words mean so many different things.’

‘The question is,’ said Humpty Dumpty, ‘which is to be master – that’s all.’

Alice’s Adventures in Wonderland and brought to mind by almost anybody reading SDK documentation for any length of time

Windows Installer has three ways of upgrading products from one version to another:

  • Major upgrades
  • Minor upgrades
  • Small updates

There are two common problems we run into when discussing upgrades given size-related terms to describe upgrades.

Version numbers

Major and minor are also commonly used to refer to particular elements in a version number (major.minor.build.patchlevel being most common).

But major upgrades can upgrade any product version number to any other version number, regardless of which parts of the version number change. For example, the WiX installers are always major upgrades for every 3.0.xxxx version; only the build portion of the version number changes with each weekly build. And the WiX v3.0 installers don’t do major upgrades down to WiX v2; we intentionally designed the installers to be able to live "side-by-side" on the same system to help migrating from WiX v2 to v3.

Minor upgrades, on the other hand, are usually used to deliver service packs; they must change some part of the product version but can’t change the major part of the product version (at least according to PatchWiz.dll error ERROR_PCW_INVALID_MAJOR_VERSION).

Small updates cannot change the product version at all.

Size matters not

Major, minor, and small also bring to mind size or breadth of a particular upgrade. To an extent, that’s true: Small updates tend to be for single-purpose "hotfix" types of fixes. Minor upgrades can update many files but can make only limited "structural" changes such as adding features and components; they can’t be used to remove components.

Major upgrades aren’t so limited: They can change anything in a product, but also support as few changes as a small update. So even if you’re changing only a few files, you can still use major upgrades. Think of major as an upper limit on the set of changes, not the lower limit on the type of upgrade you need.

Major upgrades also have the advantage of built-in support in Windows Installer. Using Upgrade and UpgradeVersion elements, you can use a "naked" .msi package to install a product for the first time or by major-upgrading a prior version of the product. Doing the same thing with a minor upgrade requires a bootstrapper executable or extra work to set the REINSTALL and REINSTALLMODE properties appropriately.

Hint: Be generous with upgrade codes

Major upgrades work across products by associating them with a single upgrade code: Version 1.0 of a product has a different product code than version 2.0 but both share the same upgrade code. The FindRelatedProducts action and MsiEnumRelatedProducts function find any products on the system with a particular upgrade code. The RemoveExistingProducts action then uninstalls the (usually older) products as part of the major upgrade cycle.

Note that FindRelatedProducts and MsiEnumRelatedProducts explicitly support finding multiple products installed on the system simultaneously. MSI doesn’t try to force you to use the same upgrade code only for products that are upgraded by major upgrades. That means it’s possible to use the same upgrade code for products that aren’t major upgrades for each other.

However, the Upgrade table supports only version and language as ways of narrowing down the products it finds. So if you use the same upgrade code for multiple products that can be installed simultaneously, FindRelatedProducts doesn’t have a way to filter out the ones you don’t want removed in a major upgrade.

You can use a custom action to remove product codes from the property specified by the UpgradeVersion/@Property attribute. Just schedule it to run after FindRelatedProducts. The cost is that you have to maintain by hand any such product codes and carry them from version to version.

The Upgrade table and FindRelatedProducts action let you search on multiple upgrade codes in a single product. So it’s always safe to use more upgrade codes if you think you might support having products side-by-side in the future — just use multiple Upgrade elements to detect and uninstall them via major upgrade.

In short, if you have products are never going to be installed at the same time, you can safely use the same upgrade code. But if you have products that might need to be installed simultaneously, give them separate upgrade codes. (Upgrade code GUIDs are cheap when you buy them in quantity from reputable online retailers.) And always test your servicing strategy before you release a product: Some things can’t be fixed in a patch.