Thoughts about setup and deployment issues, WiX, XNA, the .NET Framework and Visual Studio
All postings are provided AS IS with no warranties, and confer no rights. Additionally, views expressed herein are my own and not those of my employer, Microsoft.
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 http://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:
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:
This sample makes a couple of key assumptions that you will need to keep in mind:
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:
After creating Platformer.wxs, I had to make a couple of changes to the setup authoring information that was created:
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:
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>
<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)" />
<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>
<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:
<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>
PingBack from http://blogs.msdn.com/astebner/archive/2008/10/31/9027445.aspx
A few days ago, I wrote a blog post describing how to create an MSI-based installer for a Windows game
I’ve previously written a few posts about how to get started with Windows game deployment for games created
Great tutorial. However I had to do a few things to get the batch file to work and I think others might benefit from this info:
1. After installing the latest version of Wix (3.5), I had to copy the directory to my C:\Program Files folder. It was originally installed in C:\Program Files (x86) because I'm running a 64 bit verision of W7.
2. I had to rename the wix folder to: "Windows Installer XML v3" from "Windows Installer XML v3.5".
Once I did that, the bat files found all of the files necessary and built the test installer.
Thanks again for the great blog article!
Hi Fidelis - Thanks for writing about your experiences with the example I posted with this blog item. The sample was created and tested with WiX v3.0 instead of v3.5, so that is why the build script uses WiX tools from the v3 directory. You can definitely change that to v3.5 if you'd prefer to use WiX v3.5.
The build script in the sample should correctly handle both 32-bit and 64-bit versions of the Program Files directory. I'll try to reproduce that on my 64-bit computer and see if I need to make a fix for that.