We write a lot here at SOTC about developing applications and writing code - but really, that is only the first 90% of the battle when creating an app. The second 90% is actually getting an app off of your computer and into the hands of the people who are going to use it. A large part of that is creating an installer for your application. There are a number of possibilities for an installer for a .NET application (especially with the ClickOnce installer capability built into .NET 2 and onward). Today, we are going to take a look at creating a Windows Installer package - or, as most people know them, a MSI package.
To do this, we are going to be using WiX (Windows Installer XML), which is by far the easiest way (that I know of) to create an MSI package. For this tutorial, we will be using WiX v3.0 RC2, which is the latest build of WiX.
So first, you are going to need to get WiX installed on your computer. To do this you will need the WiX v3 installer. If you are using Visual Studio 2005, you will also need ProjectAggregator2 (this is an update for VS2005 that lets WiX interact with VS2005). If you are using Visual Studio 2008, you don't need that second component, because VS2008 already has everything WiX needs.
Ok, now that you have WiX installed, the first thing your going to want to do is create a WiX project. You do this the same way you create any project in Visual Studio - through the new project dialog:
The default new WiX project is pretty simple - it has a single file "Product.wxs". That file is where we are going to define our simple installer - so open it up! The file should look something like this at the moment:
<?xml version="1.0" encoding="UTF-8"?> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <Product Id="PUT-GUID-HERE" Name="SimpleInstaller" Language="1033" Version="184.108.40.206" Manufacturer="SimpleInstaller" UpgradeCode="PUT-GUID-HERE"> <Package InstallerVersion="200" Compressed="yes" /> <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" /> <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFilesFolder"> <Directory Id="INSTALLLOCATION" Name="SimpleInstaller"> <!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. --> <!-- <Component Id="ProductComponent" Guid="PUT-GUID-HERE"> --> <!-- TODO: Insert files, registry keys, and other resources here. --> <!-- </Component> --> </Directory> </Directory> </Directory> <Feature Id="ProductFeature" Title="SimpleInstaller" Level="1"> <!-- TODO: Remove the comments around this ComponentRef element and the Component above in order to add resources to this installer. --> <!-- <ComponentRef Id="ProductComponent" /> --> </Feature> </Product> </Wix>
A whole bunch of XML - which, really, shouldn't surprise you, considering that WiX stands for Windows Installer XML. This initial file gives a pretty good overview of the structure of a WiX file - so before we change anything, let's take a walk through it and see what everything means.
Generally, the first thing you will see in a WiX file is a
definition. This element directly corresponds to the MSI file that
results when building a WiX project. The attributes on this tag are
things like your product name, version, etc. The other two very
important properties on a
Product are the
product code) and the
These are both GUIDs - the product code should be unique for every new
product, even new versions of an existing product. The upgrade code
should be unique for a related set of products (i.e., different versions
of the same product).
By the way, when in Visual Studio, there is a handy tool to generate new GUIDs. In the Tools menu, there is an entry called "Create Guid", which pops this handy dialog:
Inside of a
Product element, there is required to be one (and only
element. Generally, I don't do much with this element - I just leave it
as is (which sets the minimum required Windows Installer version to 2.0
and turns compression of the contents of the installer on).
All the other children from this point on are optional - although it
wouldn't be a very useful installer if nothing else was specified. So on
we go to the next element - the
element. Since most installers generally install a few files, you are
going to need one of these. It represents the source of the files being
installed - generally a cabinet file. The
Id attribute (required to be
a number for
media elements) will be used later on to signify that a
source file is in this "media". The
Cabinet attribute is just the name
of the cabinet, and if you set
EmbedCab to "yes", you won't ever
actually see the cabinet file, since if will be embedded inside the MSI.
Now we get down to the meat of the installer - the
Directory elements define the directory structure that your installed
files will live in, and
Component elements are the actual pieces that
you are installing. Components are often files, but can also be things
like registry keys or shortcuts. The example above doesn't really have
much to work with in terms of these elements - so we will dive deeper
into them in a few minutes with a more complex example.
Finally, we have the
Feature is generally a grouping of components that get
installed together. You have probably seen the interface representation
of features before - they show up in almost every installer. In our
installer today, we are only going to have one feature, and the user
won't be able to make any choices. But most installers do give a few
choices - for example, the feature list from MSOffice 2003:
Ok, time to actually fill in those
Feature sections. Below is WiX file that will install a text file into
the directory "%ProgramFiles%/Switch On The Code/SimpleInstaller/" and
add a shortcut in the start menu:
<?xml version="1.0" encoding="UTF-8"?> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <Product Id="PUT-GUID-HERE" Name="SOTC-SimpleInstaller" Language="1033" Version="220.127.116.11" Manufacturer="SOTC" UpgradeCode="PUT-GUID-HERE"> <Package InstallerVersion="200" Compressed="yes" /> <Media Id="1" Cabinet="SOTC_SimpleInstaller.cab" EmbedCab="yes" /> <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFilesFolder"> <Directory Id="ProgramFilesSOTC" Name="Switch On The Code"> <Directory Id="SimpleInstallerFolder" Name="SimpleInstaller" /> </Directory> </Directory> <Directory Id="ProgramMenuFolder" /> </Directory> <DirectoryRef Id="SimpleInstallerFolder"> <Component Id="TextFileComponent" Guid="*"> <File Id="TextFile" Name="TextFile.txt" DiskId="1" Source="TextFile.txt" KeyPath="yes" /> </Component> </DirectoryRef> <DirectoryRef Id="ProgramMenuFolder"> <Component Id="TextFileShortcutComponent" Guid="*"> <Shortcut Id="TextFileShortcut" Name="TextFile" Description="Shortcut To Text File" Target="[SimpleInstallerFolder]TextFile.txt" /> <RegistryValue Root="HKCU" Key="Software\SOTC\SimpleInstaller" Name="TextFileShortcutInstalled" Type="integer" Value="1" KeyPath="yes"/> </Component> </DirectoryRef> <Feature Id="TextFileFeature" Level="1"> <ComponentRef Id="TextFileComponent" /> <ComponentRef Id="TextFileShortcutComponent" /> </Feature> </Product> </Wix>
First, let's take a look at the directory structure. The root directory element will always have the Id TARGETDIR - this is required by Windows Installer. Underneath that, it is up to you, although there are some other special Ids that you will want to know about. The two that we use here are ProgramFilesFolder represents the Program Files directory on the destination computer, and ProgramMenuFolder which represents the start menu.
Underneath the Program Files folder, we create a directory called "Switch On The Code", and then a directory named "SimpleInstaller". The id attributes of these elements can be whatever you want (as long as they are unique) - they are just used so that you can reference these folders later. The name attributes are what the folder's name gets set to.
Even though we aren't putting any directories in the Start Menu, we do call it out - this is because we will be referencing it later, and if we don't call it out here, it will be an unresolved reference.
Now, you could just drop components directly in this folder structure here. In general, though, it is good practice to separate out your directory structure from your components, even though WiX lets you nest them. This improves readability when you are installing hundreds of files. So now that the directory structure is defined, let's move on to components.
Component elements can be declared in a number of places (straight
inside features, inside directory elements, hanging out alone in a
product), but generally, you will want to define them inside a
DirectoryRef element is exactly what it sounds like - a
reference to a directory (using the id you defined for the directory
earlier in a
Directory element). Components inside a
will get placed inside the directory being referenced (generally - there
are exceptions to this rule, but we don't have to worry about those
As a general rule, you should have a component per smallest possible individual unit in your installer. In many cases, this means a component per file. Don't worry about having hundreds or even thousands of components - Windows Installer is meant to deal with this. In our installer here, we only have two components - the component for the text file and the component for the shortcut. Let's take a look at the component for the text file first.
<DirectoryRef Id="SimpleInstallerFolder"> <Component Id="TextFileComponent" Guid="*"> <File Id="TextFile" Name="TextFile.txt" DiskId="1" Source="TextFile.txt" KeyPath="yes" /> </Component> </DirectoryRef>
Components, like all other elements, get an Id. They also get a GUID, which has to be unique to this particular component. Since you could potentially have hundreds of components, WiX made it easy to get a unique GUID out - all you have to do is put a "*" in the field. WiX will replace that with an actual GUID when the MSI package gets build.
Now, for a component to be useful, it should probably do something.
That's where the child element(s) come in. In this case, we have a
which is used for copying a file. Like everything else, it gets an Id,
but then we have some more interesting properties. The
holds the destination file name, and the
Source attribute holds the
source file path. The source file path needs to be relative to where the
WiX project is getting built from - probably whatever directory you
created your WiX project in.
DiskId is a reference back up to the
Media element we defined long ago. Finally, the
when set to "yes", means that this file determines if this component is
installed or not. If the file exists at the destination, then this
component is installed, otherwise, it is not.
Oh, and because that whole component is inside that
element, the file will get placed in the directory referred to by the
DirectoryRef. And while in this case we only have one component in
DirectoryRef element, you could potentially have as many as you
Ok, so that is a simple file placement. Now how about a shortcut:
<DirectoryRef Id="ProgramMenuFolder"> <Component Id="TextFileShortcutComponent" Guid="*"> <Shortcut Id="TextFileShortcut" Name="TextFile" Description="Shortcut To Text File" Target="[SimpleInstallerFolder]TextFile.txt" /> <RegistryValue Root="HKCU" Key="Software\SOTC\SimpleInstaller" Name="TextFileShortcutInstalled" Type="integer" Value="1" KeyPath="yes"/> </Component> </DirectoryRef>
First, you can probably tell that the shortcut is going to go in the
Start Menu, since that is the directory the
DirectoryRef is referring
to. The attributes to the
Component should look familiar, but the
contents are quite different. We have a
element here, which represents the shortcut being created. Name,
Description, and Id should probably be self-explanatory at this point -
the interesting attribute is
Target. This holds what the shortcut will
be pointing at - and in this case, we want it to point at the text file
we dropped off in the previous component. To do that, we need the path
to that text file. We can get that path by using the directory id inside
of square brackets, followed by the name of the file.
[SimpleInstallerFolder] will get expanded to the actual path on the
system when the shortcut is created.
The next child is a RegistryValue element. You are probably wondering why we need a registry value - isn't all we are doing is creating a shortcut? That is true, but every component needs to have a Key Path - the thing that is used to determine if the component is really installed or not. Unfortunately, a shortcut cannot be a key path, and the recommended way to set up a key path for a shortcut is a registry key. So we are placing the value pair "TextFileShortcutInstalled":"1" at the key path "Software\SOTC\SimpleInstaller" in HKCU (HKEY_CURRENT_USER). And hey, now you know how to modify the registry in your installer.
So the only thing left now is setting up a feature, so that these components actually get installed:
<Feature Id="TextFileFeature" Level="1"> <ComponentRef Id="TextFileComponent" /> <ComponentRef Id="TextFileShortcutComponent" /> </Feature>
elements are just like
DirectoryRef elements - they refer to the
component that has the particular id. So here we have a feature that
installs both the text file and the shortcut. The
Level attribute on
Feature element is used for determining at what point optional
components get installed - but we don't have to worry about that for
now. We can deal with that when we get to creating an installer with
So that is a complete
Product definition. Now if you hit build in
Studio (and you have created a text file named "TextFile.txt" in your
project directory), WiX will build you an MSI package. And if you run
that MSI package, you will get something that looks like this:
Pretty much as simple as you can get. And once its done, you should have a shortcut in your start menu to the text file now deep in your program files directory. And part of the beauty of MSI is that for many things, you don't have to worry about the uninstall - if you go into Add/Remove programs and uninstall SOTC-SimpleInstaller, the shortcut and the text file will get removed.
Now, this is only the tip of the iceberg in terms of what you can do with WiX. There are many more complex things that you can do - anywhere from installing services to working with IIS - plus, there is a whole lot of customization that you can do to UI that is presented to the user when the app is installing. But each of those topics could be a tutorial in and of itself, so I think we will end here for today.
You can grab the Visual Studio solution for this simple installer below (although you will have to drop in your own GUIDs into the fields marked PUT-GUID-HERE before it will build).