Introducing WiXby Mike Gunderloy
Microsoft recently surprised quite a few people by releasing an application under an open source license (the Common Public License, to be precise) and hosting its source code on SourceForge, the premier open source community site. Though this isn't the first time Microsoft has shipped open source code (take a look at Services for Unix some time), the politics of the Windows Installer XML (WiX) project were heavily discussed in the wake of this release. But what about the technical aspects? It turns out that there's a lot of yummy goodness here for the developer who wants to integrate setup building into the development process, using an easily controlled XML file format. So in this article, I'm going to sidestep the politics and concentrate on what you can actually do with this stuff.
Some Basic Concepts
To start with the obvious, WiX is a set of tools for taking XML files (structured according to a particular schema) and turning them into MSI files. MSI files, of course, are the source files used by the Windows Installer Service to install new software. Using the Windows Installer Service for this purpose offers all sorts of benefits, from Add/Remove Software support to automatic nondestructive rollback if there's any problem with the installation.
At big companies like Microsoft, the people who write setup programs do nothing but write setup programs. For most of us, though, writing the setup is a sideline to the rest of our development efforts. Before you get started with a Windows Installer tool, there are a few basic concepts that you need to understand. Here are four of the key ones:
- A product is some chunk of software that you're installing. A product might be as large as Microsoft Office 2003 Enterprise in Japanese, or as small as a "Hello World" application including a single file. Each product is identified by a GUID, the product code. You aren't required to change the product code if you make a "relatively minor" change to the files for a product.
- A package contains everything that the Windows Installer Service needs to install a particular product. This includes the appropriate MSI files and any external files (such as CAB files) that the setup needs. Packages, too, are identified by GUIDs (known as package codes). If you make any change to a package, you must assign a new package code.
- A feature is a piece of functionality that makes sense to the end user of the software. The help system could be a feature, or spell-checking, or wizards. If you've ever installed software that displays a treeview of things that you can choose to install or leave out, those are individual features. Features are identified by strings that must be unique within a particular package.
- A component is a unit of software that the Installer can install. Components can include files, registry keys, shortcuts, resources, and other things. Each component is identified by a GUID, the component ID, which must be unique across all packages, regardless of product.
These four concepts just scratch the surface of the complexity of the Windows Installer Service, but they're enough to get you started with WiX. If you get deeply into the subject, you'll want to pick up a reference to the database used by the Installer. The independent InstallSite web site has a good list of books on the subject, or you can find the Windows Installer documentation in the MSDN Library.
Building Installer Files
The Windows Installer SDK includes some command-line tools and a very basic editor named Orca. With these tools, you can build a package that makes use of all of the functions of the Windows Installer Service. So why look any further? Well, the tools, though comprehensive, are not especially powerful or easy to use.
Fortunately, the SDK does not stand alone. A number of companies, such as Wise and InstallShield, have built tools that help build Installer packages. These tools help the novice or experienced author wring the most out of the Installer Service, and usually include user interface editors, ways to automatically pick up dependencies, wizards, and other powerful tools. Even Microsoft has gotten into the act; the setup projects in Visual Studio .NET are also, under the hood, just building Installer packages.
But these developer-oriented tools are not generally a good fit for an automated build process, especially one that may be called upon to determine which software to include in a setup based on other factors (such as recent check-ins, changed filenames, and so on). That's where WiX comes in. The WiX tools allow you to define the structure of a Windows Installer package using an XML file. This gives you all of the usual benefits of XML: easy editing with a variety of tools, the ability to modify the files using well-known technologies, and the ability to put the source file under source code control, for example. The WiX tools themselves are command-line tools, so they fit well with almost any build process.
A WiX Source File
So, what does a WiX source file look like? Well, here's a relatively simple one:
<?xml version="1.0" encoding="utf-8"?> <Wix xmlns="http://schemas.microsoft.com/wix/2003/01/wi"> <Product Name="Fancy Hello" Id="4AA22F5D-D457-4A58-99FB-56AFF7FFD2CC" Version="1.0.0" Manufacturer="Lark Group, Inc." Language="1033"> <Package Id="6697CCC3-B351-4DCE-820E-DEAC87F57A2A" Comments="This MSI file installs the FancyHello application" Manufacturer="Lark Group, Inc." InstallerVersion="200" Languages="1033" Compressed="yes"/> <Media Id="1" EmbedCab="yes" Cabinet="Fancy.cab"/> <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFilesFolder" Name="PFiles"> <Directory Id="FHDir" Name="Fancy" LongName="FancyHello"> <Component Id="SayHello Documentation" Guid="8637E553-C046-60E0-9101-8D35A2870C86"> <File Id="readme" Name="Readme.txt" LongName="Readme.txt" DiskId="1" src="Readme.txt"/> </Component> <Component Id="Core" Guid="B081FEDE-736F-541C-26EA-BA1015D31B37"> <File Id="Main program" Name="FANCYH~1.EXE" LongName="FancyHello.exe" DiskId="1" src="FancyHello.exe"> <Shortcut Id="Program shortcut" Directory="ProgramMenuFolder" Name="SayHello" Target="DefaultFeature" Show="normal" WorkingDirectory="TARGETDIR"/> </File> </Component> </Directory> </Directory> <Directory Id="ProgramMenuFolder" Name="PMFolder"> </Directory> </Directory> <Feature Id="DefaultFeature" Level="1"> <ComponentRef Id="SayHello Documentation"/> <ComponentRef Id="Core"/> </Feature> </Product> </Wix>
This file, when properly built with the WiX tools (I'll show you how to do that in a little bit) will install a slightly souped-up "Hello World" application consisting of a single executable file and a readme file, and it will add a shortcut to the Start->Programs menu to run the application. Before I show you how to build it, let me go through the file, piece by piece, to discuss what's in it.
<?xml version="1.0" encoding="utf-8"?> <Wix xmlns="http://schemas.microsoft.com/wix/2003/01/wi"> ... </Wix>
After the standard XML header, every WiX source file must include the
root element, with a reference to the WiX schema.
<Product Name="Fancy Hello" Id="4AA22F5D-D457-4A58-99FB-56AFF7FFD2CC" Version="1.0.0" Manufacturer="Lark Group, Inc." Language="1033"> ... </Product>
As you can no doubt guess, the
Product element defines the product that the
Installer will install. This is the first case where you can see a common WiX
pattern: the name of the element corresponds to a table in the Windows Installer
database, and the attributes correspond to the columns in the table. However,
the correspondence is not exact. For instance, the Installer database includes
foreign key columns to relate many tables, while the WiX source files use
nesting to accomplish the same relation. The WiX tools take care of defining the
appropriate foreign key values for you.
<Package Id="6697CCC3-B351-4DCE-820E-DEAC87F57A2A" Comments="This MSI file installs the FancyHello application" Manufacturer="Lark Group, Inc." InstallerVersion="200" Languages="1033" Compressed="yes"/>
Product element, you'll find a single
Package element, defining the package that corresponds to this WiX file.
<Media Id="1" EmbedCab="yes" Cabinet="Fancy.cab"/>
Media element defines the distribution media where the MSI file and any
associated CAB files will be stored. In this particular case, I set the
yes to embed the CAB file in the MSI file, so the final package
will consist of a single file. You can define more than one
Media element for a
single package; this was more important back when we used to ship software
spread across multiple diskettes.
<Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFilesFolder" Name="PFiles"> <Directory Id="FHDir" Name="Fancy" LongName="FancyHello"> ... </Directory> </Directory> <Directory Id="ProgramMenuFolder" Name="PMFolder"> </Directory> </Directory>
Directory elements define the hierarchy of directories that the Installer
Service will use with this software. Note that the
Id attributes are not
completely arbitrary. The Windows Installer defines a number of special
directories, including the two (the program files folder and the program menu folder)
that I'm using in this example.
<Component Id="SayHello Documentation" Guid="8637E553-C046-60E0-9101-8D35A2870C86"> <File Id="readme" Name="Readme.txt" LongName="Readme.txt" DiskId="1" src="Readme.txt"/> </Component> <Component Id="Core" Guid="B081FEDE-736F-541C-26EA-BA1015D31B37"> <File Id="Main program" Name="FANCYH~1.EXE" LongName="FancyHello.exe" DiskId="1" src="FancyHello.exe"> <Shortcut Id="Program shortcut" Directory="ProgramMenuFolder" Name="SayHello" Target="DefaultFeature" Show="normal" WorkingDirectory="TARGETDIR"/> </File> </Component>
Component elements and their child elements define the exact software
that will be installed. In this case, the first component consists of a single
file, while the second includes a file and a shortcut to the file. Note that WiX
will look for the files that you include relative to the XML file. The best
plan is probably to place the XML file in the same folder as your source files,
and place the WiX tools on your path.
<Feature Id="DefaultFeature" Level="1"> <ComponentRef Id="SayHello Documentation"/> <ComponentRef Id="Core"/> </Feature>
Finally (in this case), the
Feature element defines both the single feature in
this package and the mapping between features and components; in this case, the
feature includes both components.
MSI files can, of course, perform extremely complex tasks, and the corresponding WiX source files are much more complex than this one. You'll want to have both the Windows Installer SDK and the WiX documentation handy as you develop more complex files.
Building and Testing
Turning a WiX source file (which has the extension .wxs, by convention) into an
MSI file is a two-step process, with both steps being carried out by command-line tools. The first of these tools is the compiler
candle. Here's a
E:\Program Files\wix>candle fancyhellosetup.wxs Microsoft (R) Windows Installer Xml Compiler version 2.0.1615.0 Copyright (C) Microsoft Corporation 2003. All rights reserved. fancyhellosetup.wxs
Unless something goes wrong, the output from the WiX tools is very terse;
candle just echoes back the name of its input file. You can even use the
switch to suppress printing the copyright information, if you like. Terse
utilities are better for command-line automation, so this is a nice feature. The
next step is to use
light, the linker:
E:\Program Files\wix>light fancyhellosetup.wixobj Microsoft (R) Windows Installer Xml Linker version 2.0.1615.0 Copyright (C) Microsoft Corporation 2003. All rights reserved.
light didn't find any errors, you've now got an MSI file ready
to go. Though you could launch the setup by double-clicking it in Explorer, it's
worth knowing that there's a Windows Installer tool to kick it off from the
msiexec /i fancyhellosetup.msi
And when you're done testing, you can uninstall with the same utility:
msiexec /x fancyhellosetup.msi
Note that the uninstall switch is
/x, rather than the
/u that you might be
Finally, there's one more WiX tool that you should know about:
takes an existing MSI file and produces the corresponding WXS file. This is a
good way to learn how the pieces of a WiX source file fit together. But beware:
WiX source files can get very large for complex installations.
I could go into detail on many more aspects of building MSI files with WiX, but that would take an entire book to complete. Instead, let's pull back for a moment and think about the big picture. Given this toolset, the process of building an Installer package comes down to properly crafting the original XML source file. You can imagine this source file being kept under source code control, so that changes can be tracked (and quickly rolled back in case of disaster). You can also see how it could be (in part or completely) automatically generated. A complex software product might have an inventory of hundreds of features and components that it needs to install. Given the ability of Access or Excel to export to XML these days, it would make sense to maintain that inventory in a database or spreadsheet, and automatically build the relevant part of the WiX source file. Ultimately, a setup file could be created by many different developers, each working out their own part of the XML source file using automated tools.
Can this sort of scheme work? Absolutely! I can be confident in making that statement, because WiX is part of Microsoft's well-known dog food. This tool has been used by many teams inside of Microsoft for years now, and it's stood the test of time. With its release as open source, you have the added security of being able to dig in and add new features if you really need to. WiX is definitely a win-win situation for setup authors, and I expect to be digging into it in more depth the next time that I need to put together an Installer package.
Mike Gunderloy is the lead developer for Larkware and author of numerous books and articles on programming topics.
Return to ONDotnet.com