Creating an MSI-based installer for the XNA Game Studio 3.0 platformer starter kit

I previously wrote an introductory post describing Windows game deployment features that are a part of XNA Game Studio 3.0.  That post focuses mostly on ClickOnce publishing and the Visual Studio bootstrapper that can be used to create an installer to chain the .NET Framework and XNA Framework prerequisites along with Windows-based games.

I most commonly use WiX v3.0 when creating installers, so I wanted to also devote some time to explaining how to use the WiX toolset to create an MSI-based installer for a Windows game created with XNA Game Studio 3.0.

In order to write this blog post, I created a working sample that can be used to create an MSI-based installer for the platformer game that is included as a starter kit in XNA Game Studio 3.0.  In this blog post, I'll walk through how to download and build the sample, then explain the details about how I created the sample so that you can use similar techniques for the installers for your games if you choose to.

You can download the sample from https://cid-27e6a35d1a492af7.skydrive.live.com/self.aspx/Blog%7C_Tools/WiX%20Samples/wix%7C_sample%7C_xgs30%7C_game.zip.

Using the sample - preparing your system

Before being able to build an installer using the sample I created, you will need to configure your system as follows:

  1. Install Visual Studio 2008 or Visual C# 2008 Express Edition and XNA Game Studio 3.0.
  2. Install the latest build of WiX v3.0 from https://wix.sourceforge.net/releases/.
  3. Open Visual Studio and create a project using the Platformer Starter Kit (3.0) project template that is included as a part of XNA Game Studio 3.0.
  4. Build the x86 release version of the project.

Using the sample - downloading the source files and building the installer

Now that you have compiled the game, you are ready to download the sample and build an installer for the game.  You can use the following steps to do this:

  1. Download the sample from https://cid-27e6a35d1a492af7.skydrive.live.com/self.aspx/Blog%7C_Tools/WiX%20Samples/wix%7C_sample%7C_xgs30%7C_game.zip.

  2. Create a sub-folder named Setup in the Platformer game project that you created above.

  3. Extract the contents of wix_sample_xgs30_game.zip to the Setup folder.  You should end up with a folder structure like c:\Users\myusername\Documents\Visual Studio 2008\Projects\Platformer1\Platformer1\Setup with the contents of the .zip file in there.

  4. Run the script named build_wix_sample_xgs30_game.bat from the Setup folder to invoke the WiX toolset and build an MSI for the game.

    Note: If you choose to, you could add a post-build step to your game project to invoke build_wix_sample_xgs30_game.bat instead of running it manually.  This would cause your MSI to be rebuilt each time you rebuild your game.

  5. After the build completes, you will have a file named wix_sample_xgs30_game.msi.  You can double-click on it to run it and test installing a copy of the Platformer game to your system.

This sample makes a couple of key assumptions that you will need to keep in mind:

  • It expects the files that it will include into the MSI are located in a relative path of ..\bin\x86\Release.  That means that you must unzip the sample files to a sub-folder that is at the same level as the bin directory for your project.  You must also build the release (and not debug) version of the game for the installer to build correctly using this sample.
  • It expects the game executable to be named Platformer1.exe.  If you created the game with a different name, you will need to update the information in the file Platformer.wxs that is included with the sample.

Creating your own installer - Step 1: using Heat to harvest the game project output

Now that I have explained how to build the sample, I want to explain some of the behind-the-scenes details about how I created it so that hopefully you can apply similar techniques in order to use WiX v3.0 to create MSI-based installers for your own games.

The first step I took was to create a WiX source (.wxs) file to describe the files and directory structure that I want to use when installing the game.  The installer needs to essentially mimic the output from the game project's build process (the \bin\x86\release\ directory for example), but there are a lot of files in that folder, and I didn't want to author all of these files into WiX syntax manually.  Fortunately, the WiX toolset contains a tool named Heat that allows you to harvest resources and automatically create WiX setup authoring for you.

In this case, I ran Heat with command line switches to cause it to recursively harvest all of the files in the \bin\x86\release\ directory and generate a .wxs file from it.  To do that, I used the the following steps:

  1. Click on the Start menu, choose Run, type cmd and click OK
  2. Change the cmd prompt directory to my game project's root directory by running a command like the following:  cd /d c:\users\myusername\Documents\Visual Studio 2008\Projects\Platformer1\Platformer1
  3. Run this command to invoke Heat and harvest the \bin\x86\release\ directory for the game:  "%ProgramFiles%\Windows Installer XML v3\bin\heat.exe" dir -gg -sfrag bin\x86\release -out Platformer.wxs

After creating Platformer.wxs, I had to make a couple of changes to the setup authoring information that was created:

  1. Change the Directory element to a DirectoryRef so that it can be included in my main .wxs file.  To do that, I found this line:

    <Directory Id="release" Name="release">

    and changed it to this (the exact ID value is important here - I will explain that in more detail below):

    <DirectoryRef Id="APPLICATIONFOLDER">

  2. Change each of the Source values to be relative paths instead of absolute paths - this isn't strictly necessary, but it makes the .wxs file more portable so it will work on other systems that don't have the exact same folder structure.  You can do a find-and-replace to update all of the Source values at once.

  3. Add an icon that will be displayed for the game in Add/Remove Programs.  I chose the icon that is a part of the Platformer game starter kit for this purpose.  To do that, I added the following lines to Platformer.wxs inside the Fragment but outside of the DirectoryRef:

    <Icon Id="ProductIcon" SourceFile="..\Game.ico" />
    <Property Id="ARPPRODUCTICON" Value="ProductIcon" />

  4. Remove the Component, File and ComponentRef elements for the file Platformer1.pdb so it will not be included in my installer.

Creating your own installer - Step 2: creating a main .wxs file that references the Heat output

After generating the setup authoring for the game project output, I created a main .wxs file to represent my MSI and to reference the contents of the Platformer.wxs file that I created above.  The file wix_sample_xgs30_game.wxs is the main .wxs file for the sample I created, and most of its contents are boilerplate WiX syntax.  There are a few specific items that I added to the .wxs file in order to cause the WiX build process to include the game payload that was created by Heat in Platformer.wxs:

  1. I defined the following Directory structure, which is used as the default install path for the game in the setup UI:

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFilesFolder" Name="Program Files">
        <Directory Id="APPLICATIONFOLDER" Name="Sample XNA Framework 3.0 Game" />
      </Directory>
    </Directory>

    Note - the Id value of the game's root directory needs to be set to APPLICATIONFOLDER just like the Id value of the DirectoryRef that we added to Platformer.wxs above.  This is how WiX will know to include all of the files and sub-folders under this directory into your MSI at build time.

  2. I defined the following Feature, and included a reference to the ComponentGroup1 item that was created by Heat in Platformer.wxs:

    <Feature Id="Payload" Title="!(loc.Feature_Title)" Level="1">
      <ComponentGroupRef Id="ComponentGroup1" />
    </Feature>

    Note - the Id value of the ComponentGroupRef listed here must match the Id of the ComponentGroup in Platformer.wxs in order to cause WiX to include all of the files and sub-folders into your MSI at build time.

Creating your own installer - Step 3: adding prerequisite checking

After adding the payload for the game to the installer, my next step was to add some prerequisite checks to make sure that users will not be allowed to install the game if system components needed to run the game are missing.  In a future blog post, I will explain how to build a VS bootstrapper package to automatically install the prerequisites if they are missing, but for now, I am just going to demonstrate how to check for the .NET Framework 2.0 and the XNA Framework Redistributable 3.0 and block installation if they are missing.  Note that even if you add logic later on to automatically install these prerequisites, it is a good idea to have these blocks in the MSI in case a user tries to skip running the bootstrapper and installs the MSI directly.

The following logic allows you to set property values that identify the .NET Framework 2.0 and XNA Framework Redistributable 3.0 install state:

<PropertyRef Id="NETFRAMEWORK20" />

<Property Id="XNAFRAMEWORK30" Secure="yes">
  <RegistrySearch Id="XnaFramework30RegLocator" Root="HKLM" Key="SOFTWARE\Microsoft\XNA\Framework\v3.0" Name="Installed" Type="raw" />
</Property>

Note - the NETFRAMEWORK20 property is included as a part of the WixNetfxExtension in WiX v3.0, so I am including it as a PropertyRef instead of defining it myself.

After defining the detection properties, we next need to create type 19 custom actions to block the user from installing the game if the prerequisites are not found on the system.  That requires adding the following authoring:

<CustomAction Id="CA_CheckForNetfx20" Error="!(loc.LaunchCondition_Netfx20)" />

<CustomAction Id="CA_CheckForXnafx30" Error="!(loc.LaunchCondition_Xnafx30)" />

Note: The exact error messages are defined in a separate WiX localization (.wxl) file in order to make it easier to create localized versions of the installer in the future if we choose to.

After creating the custom actions, we now need to schedule them so they will run at the appropriate times during setup.  That is accomplished by adding the following information to the sequence tables:

<InstallExecuteSequence>
  <LaunchConditions After="AppSearch"/>
  <Custom Action="CA_CheckForNetfx20" After="LaunchConditions">NOT NETFRAMEWORK20 AND NOT Installed</Custom>
  <Custom Action="CA_CheckForXnafx30" After="CA_CheckForNetfx20">(NOT XNAFRAMEWORK30 = "#1") AND NOT Installed</Custom>
</InstallExecuteSequence>

<InstallUISequence>
  <LaunchConditions After="AppSearch"/>
  <Custom Action="CA_CheckForNetfx20" After="LaunchConditions">NOT NETFRAMEWORK20 AND NOT Installed</Custom>
  <Custom Action="CA_CheckForXnafx30" After="CA_CheckForNetfx20">(NOT XNAFRAMEWORK30 = "#1") AND NOT Installed</Custom>
</InstallUISequence>

Creating your own installer - Step 4: finishing touches

After creating the main .wxs file and adding the necessary references to the game payload and the custom actions to check for prerequisites, I also added the following items to provide the user experience that I wanted to support for this game installer:

  1. I wanted to build a self-contained MSI that includes all of the game payload.  To accomplish that, I added the following Media entry that tells WiX to embed the payload as a cabinet file inside of the MSI instead of packaging the payload as loose files next to the MSI:

    <Media Id="1" Cabinet="wix_sample_xgs30_game.cab" EmbedCab="yes" />

  2. I chose the built-in WixUI_InstallDir dialog set for the setup UI so that I would not have to create any custom setup UI pages.  This dialog set includes a welcome page, a license agreement page, an installation directory selection page, a confirmation page, a progress page and a completion page (plus relevant error dialogs for failure cases).  I added the following information to the .wxs file to accomplish this:

    <UIRef Id="WixUI_InstallDir"/>
    <Property Id="WIXUI_INSTALLDIR" Value="APPLICATIONFOLDER" />
    <WixVariable Id="WixUILicenseRtf" Value="License.rtf" />

    Notes - The Property named WIXUI_INSTALLDIR must have a Value of APPLICATIONFOLDER to match the value used above - this is how the default install path is set for the setup UI.

    Also, the sample I posted includes a default license.rtf file to display in the license agreement text box.  You can replace that file with your own license terms when creating your own games by updating the Value of the WixVariable named WixUILicenseRtf to point to your own license file.

  3. I added logic to this MSI to enable Windows Installer major upgrades - this allows me to build a new version of the MSI that will automatically uninstall older versions if they are installed on the user's system.  This is an optional step, but I typically include it when I create any installer in order to give myself more options for updating the application in the future.  I added the following information to accomplish this:

    <Upgrade Id="!(loc.Property_UpgradeCode)">
      <UpgradeVersion Minimum="$(var.ProductVersion)" OnlyDetect="yes" Property="NEWERVERSIONDETECTED" />
      <UpgradeVersion Minimum="1.0.0" IncludeMinimum="yes" Maximum="$(var.ProductVersion)" IncludeMaximum="no" Property="OLDERVERSIONBEINGUPGRADED" />
    </Upgrade>

    <InstallExecuteSequence>
      <Custom Action="CA_BlockOlderVersionInstall" After="FindRelatedProducts">
        <![CDATA[NEWERVERSIONDETECTED]]>
      </Custom>
      <RemoveExistingProducts After="InstallInitialize" />
    </InstallExecuteSequence>

    <InstallUISequence>
      <Custom Action="CA_BlockOlderVersionInstall" After="FindRelatedProducts">
        <![CDATA[NEWERVERSIONDETECTED]]>
      </Custom>
    </InstallUISequence>

Creating your own installer - Advanced scenario 1: per-user installer

The sample that I posted creates a per-machine MSI that requires administrative privileges (and UAC elevation on Windows Vista).  Many XNA Framework-based Windows games can install and run as per-user applications.  It is possible to convert this sample from a per-machine MSI into a per-user MSI.  There is a set of general steps that need to be taken to create a per-user MSI in WiX v3.0 in this blog post.

Creating your own installer - Advanced scenario 2: Windows Vista Game Explorer integration

WiX v3.0 includes built-in logic to allow you to integrate your game with the Windows Vista Game Explorer.  There is an introduction to this WiX v3.0 feature in this blog post.  In addition, Bob Arnson recently enhanced the WixGamingExtension to support using separate files for the game executable and the GDF resource file.  This fix is available in WiX builds starting with 3.0.4707.0.

<update date="3/21/2009"> Fixed broken link to sample installer files. </update>