





















































In this article by Nick Ramirez, author of the book WiX Cookbook, we will see how we tackle the trouble of getting any bit of code from development to production. WiX solves this problem for its own code by allowing it to be built using a variety of workflows. As part of the WiX toolset, we get the compiler and linker needed to create an MSI installer. If we're using Visual Studio then we also get project templates that use these tools on our behalf so that the entire build process is effortless. If we're trying to fit WiX into an automated deployment pipeline, we can either call the compiler and linker from the command line or use ready-made MSBuild tasks.
(For more resources related to this topic, see here.)
It's possible to work with WiX outside of Visual Studio, but within it, you'll benefit from the project templates; IntelliSense and shortcuts to the compiler and linker settings are available on the project's properties. The only downside is that WiX doesn't work with Visual Studio Express. However, its installer will give you the compiler and linker so that you can still get work done even if you're using Notepad to write the markup. SharpDevelop, a free and open source IDE, also supports WiX projects.
Getting WiX up and running starts with downloading and running its installer. This is a one-stop shop to update Visual Studio, getting the compiler and linker as well as other utilities to work with MSI packages. WiX supports Visual Studio 2005 and later, including Visual Studio 2013, which we'll cover here. In this recipe, we will download and install WiX and create our first setup project.
To prepare for this recipe, install Visual Studio 2013 and close it before installing WiX.
Download and install the WiX toolset to get access to new project templates, IntelliSense, and project properties in Visual Studio. The following steps will guide you:
<?xml version="1.0" encoding="UTF-8"?> <Wix > <Product Id="*" Name="My Software" Language="1033" Version="1.0.0.0" Manufacturer="My Company" UpgradeCode="889e2707-5235-4d97-b178-cf0cb55d8ab8"> <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" /> <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." /> <MediaTemplate /> <Feature Id="ProductFeature" Title="MyFirstWixProject" Level="1"> <ComponentGroupRef Id="ProductComponents" /> </Feature> </Product> <Fragment> <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFilesFolder"> <Directory Id="INSTALLFOLDER" Name="My Software" /> </Directory> </Directory> </Fragment> <Fragment> <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER"> <!-- TODO: Remove the comments around this Component element
and the ComponentRef below in order to add resources
to this installer. --> <!-- <Component Id="ProductComponent"> --> <!-- TODO: Insert files, registry keys, and other resources here. --> <!-- </Component> --> </ComponentGroup> </Fragment> </Wix>
The WiX team has always worked quickly to keep up with the latest versions of Visual Studio. For example, WiX 3.9 supports Visual Studio 2013. When we launched the installer, it checked which versions of Visual Studio were present and registered its project templates with all that were compatible.
Behind the scenes, WiX introduces a new project type that has a .wixproj file extension. This project file contains MSBuild markup, which points to the WiX compiler and linker. Other IDEs, such as SharpDevelop, can take advantage of these project files to build MSI packages too.
The Product.wxs file contains everything we need to get started with writing WiX markup. The best coding practices for how to structure a WiX file have been defaulted for you. For example, the Directory elements are separated into a Fragment element so that directories are decoupled from the files that will go into them. A ComponentGroup has been set up with a comment guiding you to add Component elements to it. Each version of WiX brings a better Product.wxs file with it.
If you were curious about what effect changing the version of the .NET framework listed in the drop-down list at the top of the New Project window would have, the answer, at least for setup projects, is nothing at all. A WiX file contains XML and is compiled with a specialized WiX compiler, so the version of .NET that we select will ultimately be ignored. That's not to say that it doesn't make a difference for any of the other project types. For example, C# Custom Action Project will have a dependency on the version of .NET that's selected. Anyone who uses the installer that in turn uses that custom action will need to have that version of .NET installed.
After setting up our WiX project, the first thing we'll probably want to do is package up the files that we plan to install. Since we're working in Visual Studio, we'll likely want to include the output of other projects such as the .exe file that's created from a console application project. At first, we could try hardcoding the path to the file:
<Component Id="cmpMyConsoleAppEXE" Guid="{882DB6AA-1363-4724-8C43-2950E7ABECD4}"> <File Source="..MyConsoleAppbinDebugMyConsoleApp.exe" /> </Component>
Although this works, it's a bit brittle and will break if the path to the file changes. Instead, we can use a preprocessor variable to store the path and allow Visual Studio to keep it up-to-date through the power of project references. In this recipe, we'll reference a console application's output and use a preprocessor variable to include that output in our installer.
To prepare for this recipe, create a new WiX setup project and name it ConsoleAppInstaller.
Use a preprocessor variable to get the path to a project's output with the following steps:
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER"> <Component Id="cmpTestApplicationEXE" Guid="{6E2A6370-4784-4CF3-B42B-AA2D29EA5B1B}"> <File Source="$(var.TestApplication.TargetDir)TestApplication.exe" /> </Component> </ComponentGroup>
When we referenced the C# console application within the WiX setup project, the preprocessor variable $(var.[ProjectName].TargetDir) was made available to us, where ProjectName in this case is TestApplication. TargetDir points to the output directory of the console application project where our compiled TestApplication.exe file can be found.
Other preprocessor variables are also made available. For example, $(var.[ProjectName].TargetFileName) gives you the name of the compiled application, which for us would be TestApplication.exe. A full list of these variables can be found at http://wixtoolset.org/documentation/manual/v3/votive/votive_project_references.html.
Another benefit of referencing the console application project in this way is that it ensures it is compiled before our setup project is. This way, our installer always includes the most up-to-date version of the application.
The GUID used for the Guid attribute on the Component element in this example can be any GUID, not just the one listed. You can generate a new one in Visual Studio by navigating to Tools | Create GUID. Use Registry Format as the GUID's format. More information can be found at http://wixtoolset.org/documentation/manual/v3/howtos/general/generate_guids.html.
You can also set the Guid attribute to an asterisk (*) or omit it altogether and WiX will set the GUID for you. You should choose your own if you plan on authoring a patch file for the application in the future or if the contents of Component don't contain an element that can be marked as a KeyPath element.
As a project grows in complexity and size, we may end up with different teams building different parts of the software in relative isolation. Each team may want to control how their module will be installed or, during development, install only the modules that their code depends upon into their dev environment. To handle these scenarios, we can split our installer into chunks of WiX code called setup libraries.
A setup library can be compiled independently and plugged into the main, monolithic setup project later. We can also include the library in a team-owned setup project that only contains the modules required by the team. In essence, we can mix and match libraries wherever we need them to create installers for different purposes.
You might also want to share some complex installer markup, such as a user interface, with other installers, and a library is the perfect way to do this. Although it's outside the scope of this article, setup libraries are also used when building custom WiX extensions. In this recipe, we'll see how to create a setup library and include it in our setup project.
To prepare for this recipe, create a setup project and call it SetupLibraryInstaller.
Add a setup library to the solution and reference it in a setup project. The following steps show how to do this:
<?xml version="1.0" encoding="UTF-8"?> <Wix > <Fragment> <DirectoryRef Id="INSTALLFOLDER"> <Directory Id="SampleComponentsDirectory" Name="Sample Components" /> </DirectoryRef> <ComponentGroup Id="SampleComponentGroup" Directory="SampleComponentsDirectory"> <Component Id="cmpSampleTextFileTXT" Guid="{5382BC02-4484-4C9B-8734-A99D20632EA9}"> <File Source="SampleTextFile.txt" /> </Component> </ComponentGroup> <Feature Id="SampleFeature"> <ComponentGroupRef Id="SampleComponentGroup" /> </Feature> </Fragment> </Wix>
<Feature Id="ProductFeature" Title="ConsoleAppInstaller" Level="1"> <ComponentGroupRef Id="ProductComponents" /> </Feature> <FeatureRef Id="SampleFeature"/>
Our setup library contains WiX markup to install a single text file called SampleTextFile.txt. Ordinarily, when you build a library like this, the source files don't get stored within it. Instead, only the WiX markup is compiled without any of the source files it refers to. In that case, we would have had to copy SampleTextFile.txt to the setup project's directory too, so that it can be found at link-time when compiling the installer.
However, because we added the -bf flag, which stands for bind files, to the Librarian settings, the text file was serialized and stored within the library. The -bf flag will handle serializing and storing any type of file including executables, images, and other binary data. Setup libraries are compiled into files with a .wixlib extension.
The markup we added to the library created a component, directory, and feature for the text file. To integrate the new directory with the existing directory structure as defined by our setup project, we chose to reference INSTALLFOLDER with a DirectoryRef element. Just be sure that there's a corresponding Directory element in your setup project that has this name. At link time, the DirectoryRef element in the library is merged with the Directory element in the setup project by matching their IDs. Once we had this, we were able to add a new subdirectory within the INSTALLFOLDER directory called Sample Components. After installation, we can see that the new directory was created and it contains our text file:
To be sure that our library gets compiled before our setup project, we referenced it within the setup project using the References node. Then, to create a link to the library, we included a FeatureRef element in Product.wxs, which had an ID matching the Feature defined in the library. This pulls the Feature with all of its components into the installer.
The setup libraries might contain more than just components, features, and directories. For example, they might define markup for a user interface using a UI element, which could then be linked to our installer with a UIRef element. Basically, if you can find a corresponding *Ref element, such as DirectoryRef, UIRef, ComponentGroupRef, or FeatureRef, then you'll be able to separate that type of element into a library and use its *Ref element to link it to the setup project.
Even if you can't find a corresponding *Ref element, as long as you have a reference of some kind, such as Property and PropertyRef, the rest of the elements in the library will be carried along with it into the installer. So, at the very least, you could include a single Property in the library and use that as the link between the library elements and the installer.
The WiX Toolset places its compiler and linker in C:Program Files (x86)WiX Toolset v3.9bin. This is fine when compiling on your own machine but becomes a concern when you'd like to share your project with others or have it compile on a build server. WiX will have to be installed on each computer that builds the project.
Alternatively, we can store the WiX tools in source control, and then whoever needs to build a setup project can get everything they need by cloning the repository. This will also help us keep a handle on which version of WiX we're compiling against on a project-by-project basis.
In this recipe, we'll store the WiX binaries in a fictitious source control directory on the C: drive. We'll then update the .wixproj file of a setup project to use the MSBuild tasks stored there. I will be using a server with the Windows Server 2012 R2 operating system installed on it. You should be able to follow along with other versions of Windows Server.
To prepare for this recipe, perform the following steps:
Download the WiX binaries and update your setup project to use the included MSBuild tasks:
<PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">x86</Platform> <ProductVersion>3.9</ProductVersion> <ProjectGuid>f80ca9fc-8e42-406e-92f9-06e484e94d67</ProjectGuid> <SchemaVersion>2.0</SchemaVersion> <OutputName>BuildMachineInstaller</OutputName> <OutputType>Package</OutputType> <WixToolPath>C:SourceControlWiX39</WixToolPath> <WixTargetsPath>$(WixToolPath)wix.targets</WixTargetsPath> <WixTasksPath>$(WixToolPath)WixTasks.dll</WixTasksPath> <WixTargetsPath Condition=" '$(WixTargetsPath)' == '' AND
'$(MSBuildExtensionsPath32)' != '' ">$(MSBuildExtensionsPath32)
MicrosoftWiXv3.xWix.targets</WixTargetsPath> <WixTargetsPath Condition=" '$(WixTargetsPath)' == ''
">$(MSBuildExtensionsPath)MicrosoftWiXv3.x
Wix.targets</WixTargetsPath> </PropertyGroup>
cd C:SourceControlBuildMachineInstaller msbuild BuildMachineInstaller.sln
We started with a blank slate of a freshly installed Windows Server 2012 R2 operating system. Therefore, we had to install all the required software including .NET Framework 3.5 and Microsoft Build Tools 2013. The latter gives us the MSBuild engine, whose path we included in the computer's PATH environment variable.
Next, we downloaded the WiX binaries and copied them to C:SourceControl. With a source control system, these files could be shared among all computers that need to compile our setup projects. We also had to update our project's .wixproj file so that it knew where to find these WiX binaries. This is accomplished by adding three MSBuild properties: WixToolPath, WixTargetsPath, and WixTasksPath. The first property sets the path to the WiX binaries, the second to the wix.targets file, and the third to WixTasks.dll. With all of this setup out of the way, we opened a command prompt, navigated to the folder where our solution file was on the build server, and compiled it using MSBuild.
WiX has excellent integration with Visual Studio, but that shouldn't stop you from using it in other IDEs. We ought to be able to create an installer using only Notepad and the WiX compiler and linker if we wanted to. Luckily, WiX gives us the freedom to do this. In this recipe, we'll write a simple .wxs file and compile it into an MSI package using Candle, which is the WiX compiler, and Light, which is the WiX linker.
To prepare for this recipe, perform the following steps:
<?xml version="1.0" encoding="UTF-8"?> <Wix > <Product Id="*" Name="My Software" Language="1033" Manufacturer="My Company" Version="1.0.0.0" UpgradeCode="8c7d85db-b0d1-4a9a-85ea-130836aeef67"> <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" /> <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." /> <MediaTemplate EmbedCab="yes" /> <Feature Id="ProductFeature" Title="The main feature" Level="1"> <ComponentGroupRef Id="ProductComponents" /> </Feature> </Product> <Fragment> <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFilesFolder"> <Directory Id="INSTALLFOLDER" Name="My Software" /> </Directory> </Directory> </Fragment> <Fragment> <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER"> <Component Id="cmpMyTextFileTXT" Guid="{A4540658-09B6-46DA-8880-0B1962E06642}"> <File Source="MyTextFile.txt" /> </Component> </ComponentGroup> </Fragment> </Wix>
Open a command prompt and use candle.exe and light.exe to compile and link our WiX source file:
cd C:MyProject
"%WIX%bincandle" *.wxs -o obj
"%WIX%binlight" obj*.wixobj -o binCommandLineInstaller.msi
When we installed the WiX toolset, it gave us the WiX compiler, which is candle.exe, and linker, which is light.exe. These are the only tools we need to create an MSI from our WiX source file, Product.wxs. From the command line, we navigated to the directory where our source file was and then used Candle and Light to compile and link the file to create an MSI installer.
The first argument we passed to Candle was *.wxs. This selects all the .wxs files in the current directory and includes them in the compilation. Next, the -o argument tells Candle where to send the output of the compilation step. In this case, we sent it to a directory called obj. Note that the directory name ends in a backslash so that Candle knows that it's a directory. If it didn't exist before, it will be created.
The output of the Candle command was a file called Product.wixobj. This was an intermediate file that was picked up by light.exe in the next step. The first argument we passed to Light was the location of the .wixobj files: obj*.wixobj. By using an asterisk, we select all the .wixobj files in the obj directory. The -o argument tells Light where to create the MSI file and what to name it. In this case, we create a file called CommandLineInstaller.msi.
Another file called CommandLineInstaller.wixpdb was also created. This can be used when building patch files. You can learn more by reading Peter Marcu's blog post WiX: Introducing the WixPdb at http://petermarcu.blogspot.com/2008/02/wix-introducing-wixpdb.html.
There are a number of arguments that can be passed to Candle and Light that you might want to get to know. Passing the -? flag to either will give you a list of all the available options:
"%WIX%bincandle" -? "%WIX%binlight" -?
We used the %WIX% system environment variable to resolve the path to the WiX bin directory, where candle.exe and light.exe are present. This variable is added when you install the WiX toolset and resolves to C:Program Files (x86)WiX Toolset v3.9. It will not be present if you are using the WiX binaries directly without installing the WiX toolset.
This article helped you to build your WiX projects right, whether that means from Visual Studio, the command line, or on a build server with automation. We also saw how to reference the output of other projects that were included in the installer and how to separate WiX markup into libraries.
Further resources on this subject: