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.

Localizing more than strings in WiX v3.6

Localizing an MSI package—primarily the user interface but also bits like the product name, shortcut names, and other user-visible, localizable strings—has been pretty much unchanged since WiX v2.0:

  • Localizable strings are specified in the appropriate attributes in your WiX authoring using !(loc.stringid) syntax.
  • Strings are specified by id in per-language WixLocalization (.wxl) files.
  • Light, the WiX linker, takes a list of cultures to use when resolving !(loc.stringid) in the authoring.

As you can see, localization is all about the strings. However, strings can vary dramatically in length among languages. In the WixUI dialog library, controls were sized to handle string lengths for all the languages WixUI supported. Unfortunately, that resulted in some controls being far wider than necessary. The Install button, for example, is wider than the other buttons on the same dialogs. We hadn’t had to resize any controls in WixUI for a while—and then a bug said otherwise.

Rather than yet again resizing a control, WiX v3.6 gets a new feature so we never have to do that again. In WiX v3.6, the WixLocalization schema has been extended with a UI child element that lets the localization for a particular culture change the attributes of an individual control or an entire dialog. For example, a later bug indicated the need for another big button in WixUI_Advanced. The fix in the ru-RU localization file was easy:

<UI Dialog="AdvancedWelcomeEulaDlg" Control="Print" X="50" />
<UI Dialog="AdvancedWelcomeEulaDlg" Control="Advanced" X="124" Width="85" />

The first line moves the Print button to the left, to make room for a bigger Advanced button, which the second button supplies. The dialog and control attributes identify a particular control.

The WixLocalization UI element (unrelated to the UI element in the WiX namespace) lets you:

  • Resize a control using the Height and Width attributes.
  • Move a control using the X and Y attributes.
  • Change the control’s localization-related attributes: LeftScroll, RightAligned, and RightToLeft.
  • Change the control’s text using the inner text of the UI element.

You can also use the UI element on dialogs by omitting a Control attribute:

  • Resize a dialog using the Height and Width attributes.
  • Change the dialog’s centering values using the X and Y attributes.
  • Change the dialog’s title using the inner text of the UI element.

Don’t forget native code

Though localization files are most often used inside MSI packages using WixUI dialog sets, they’re also used extensively in the WixStandardBootstrapperApplication for Burn. And even if you use a managed bootstrapper application, you’re still using WixStandardBootstrapperApplication when the managed host needs to install .NET.


Everybody could use a good Burn

Rick Brewster of the Paint.NET team blogged about the pain of getting Paint.NET installed on a system without the .NET Framework. It’s fair to say that for a system running Windows XP without optional updates, installing the latest and greatest Framework is an annoying exercise. Part of the problem (blame) falls on the shoulders of the Framework folks: The Framework installer should install MSI 3.1 for users; asking them to go spelunking on the Microsoft Download Center for the MSI 3.1 redistributable, then run it and reboot, is rude at best.

Rick’s proposed solution for the next release of Paint.NET is OK, though it has the downside of showing several very different UI experiences:

  • A "confirmation" dialog that tells users that prerequisites will be installed.
  • The OS-component update dialog (for MSI 3.1) that most users no longer see, because it’s hidden behind Windows Update.
  • The .NET Framework Client Profile bootstrapper UI, which is slightly different than the OS-component update dialog.
  • The Paint.NET installer configuration UI, which is slightly different than the others.
  • The Paint.NET installer UI itself, which uses MSI basic UI.
  • If needed, a "reboot needed" dialog, which uses slightly different UI.

A key scenario for Burn is to enable this scenario (with the Client Profile or "normal" .NET Framework installation). Part of being successful with that scenario is to have an integrated experience, which means having a consistent user interface and one progress bar.

We’re not there yet, but know that we’re working hard to solve this problem.

WiX Burn, Volume 1: Layers

Burn is the name of the new WiX chainer. (Burn is brought to you by the letter B, which stands for bootstrapper.) Though we’re working to ship WiX v3 without Burn, work on Burn continues — it turns out that several members of the WiX virtual team need a chainer for their day jobs. As I’m one of them, I thought I’d blog about the work I’m doing. To start that, this post covers the layers in Burn and how they fit together:

  • Core: The Burn core is where the actual chaining and installation code lives. This is, not surprisingly, the biggest chunk of code in Burn, with several thousand lines of code. The core supports installing .msi and .exe packages, with .msp patch package support planned. The core is a static library.
  • UX: UX, which stands for User eXperience, handles user interface. Burn includes StdUx, which provides a basic UI. StdUx can be replaced by any DLL that adheres to the Burn UX interface.
  • Stub: The Burn stub is an .exe that handles standard command-line processing and startup. It loads the manifest, UX DLL, and packages — any of which might be embedded in the .exe — and lets the UX take over.
  • BurnExe: BurnExe is the manifest compiler and bundle builder. The package manifest is an XML file that specifies the list of packages to be installed; BurnExe resolves references to packages and extracts metadata like the product code and product version. BurnExe also optionally builds a bundle — a copy of a stub with embedded data. For example, right now, BurnExe supports embedding the resolved manifest so setup.exe can be signed to help ensure the manifest hasn’t been tampered with. The plan is to also support embedding the UX DLL and packages to provide a single setup.exe download.

Any layer of Burn can be replaced. For example, I expect someone on my team to create a custom UX for Train Simulator 2. The Burn design focuses customizability in the UX so the other layers can be used as-is.

There’s plenty more work to do, so that’s it for today. More to come as more of Burn matures!