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.
Since we released XNA Game Studio 3.0, I've heard of a few problems from customers via my blog and the Creators Club forums related to deployment of Windows game projects that include custom content processors. Most of the problems I've heard of so far involve using ClickOnce to deploy a game, then receiving the following error message when attempting to run the game on another computer that doesn't have XNA Game Studio 3.0 installed:
Unable to install or run the application. The application requires that assembly Microsoft.Xna.Framework.Content.Pipeline be installed in the Global Assembly Cache (GAC) first.
This error is typically caused by including a reference to the custom content processor in the Windows game project even though the processor is only needed at build time. If the processor is only needed at build time, you only need to include a reference to it in your content project. Doing this will prevent any dependencies on content pipeline assemblies while running your game. This is important because the XNA Framework Redistributable 3.0 does not contain content pipeline assemblies, which means that the only way to run a game that requires content pipeline assemblies at runtime (and not just at build time) is to install XNA Game Studio 3.0 on the target system.
I have also heard of one issue where someone fixed the content pipeline assembly reference issue described above, but then ran into an exception stating that the type ContentTypeReader cannot be found when deploying their their game to another computer. Custom content processors can include several parts - an importer, a processor, a type writer and a type reader. Of these, the type reader and any types used by your game need to be deployed to the target system along with your game binaries and content. The importers, processors and type writers are only needed at build time and do not need to be deployed to the target system.
In order to avoid deployment issues, if you have a custom content processor that includes both build time and runtime components, you can split your custom content processor into the following:
To better understand the content pipeline and the split between build time and runtime components, I'd suggest reading the recent post that Shawn Hargreaves wrote at http://blogs.msdn.com/shawnhar/archive/2008/11/24/content-pipeline-assemblies.aspx. The post includes the following information:
I have run into a few reports of .NET Framework 3.0 and 3.5 installation failures recently that have exhibited the same set of symptoms. I wanted to describe this issue in more detail so that folks who have followed the steps in my previous post about diagnosing .NET Framework 3.0 and 3.5 installation failures related to the ServiceModelReg.exe custom action will recognize this pattern and have some additional workarounds to try to get the .NET Framework to install successfully on their systems.
Diagnosing the issue
When the .NET Framework 3.0 or 3.5 setup fails, I recommend using steps like the ones listed at http://blogs.msdn.com/astebner/archive/2008/03/28/8342307.aspx to narrow down the root cause. Recently, I have heard from a few customers who have used these steps, and when they get to step 4 and look in the event logs on their system, they see errors like the following:
An error occurred while creating the MSI-style log file at 'C:\Documents and Settings\myusername\Local Settings\Temp\':System.IO.IOException: Unknown error "-1". at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share) at System.ServiceModel.Install.MsiStyleLogWriter..ctor(String logEntryPrefix). System.IO.IOException: Unknown error "-1". at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access) at System.CodeDom.Compiler.TempFileCollection.EnsureTempNameCreated() at System.CodeDom.Compiler.TempFileCollection.AddExtension(String fileExtension, Boolean keepFile) at System.CodeDom.Compiler.TempFileCollection.AddExtension(String fileExtension) at Microsoft.CSharp.CSharpCodeGenerator.FromSourceBatch(CompilerParameters options, String[] sources) at Microsoft.CSharp.CSharpCodeGenerator.System.CodeDom.Compiler.ICodeCompiler.CompileAssemblyFromSourceBatch(CompilerParameters options, String[] sources) at System.CodeDom.Compiler.CodeDomProvider.CompileAssemblyFromSource(CompilerParameters options, String[] sources) at System.Xml.Serialization.Compiler.Compile(Assembly parent, String ns, CompilerParameters parameters, Evidence evidence) at System.Xml.Serialization.TempAssembly.GenerateAssembly(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, Evidence evidence, CompilerParameters parameters, Assembly assembly, Hashtable assemblies) at System.Xml.Serialization.TempAssembly..ctor(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, String location, Evidence evidence) at System.Xml.Serialization.XmlSerializer.GenerateTempAssembly(XmlMapping xmlMapping, Type type, String defaultNamespace) at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace) at System.Xml.Serialization.XmlSerializer..ctor(Type type) at System.ServiceModel.Install.Configuration.ConfigurationHandlersInstallComponent..ctor(ConfigurationLoader configLoader) at System.ServiceModel.Install.Configuration.ConfigurationHandlersInstallComponent.CreateNativeConfigurationHandlersInstallComponent() at Microsoft.Tools.ServiceModel.ServiceModelReg.BuildActionQueue() at Microsoft.Tools.ServiceModel.ServiceModelReg.Run(String[] args) at Microsoft.Tools.ServiceModel.ServiceModelReg.TryRun(String[] args).
An error occurred while creating the MSI-style log file at 'C:\Documents and Settings\myusername\Local Settings\Temp\':System.IO.IOException: Unknown error "-1". at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share) at System.ServiceModel.Install.MsiStyleLogWriter..ctor(String logEntryPrefix).
System.IO.IOException: Unknown error "-1". at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access) at System.CodeDom.Compiler.TempFileCollection.EnsureTempNameCreated() at System.CodeDom.Compiler.TempFileCollection.AddExtension(String fileExtension, Boolean keepFile) at System.CodeDom.Compiler.TempFileCollection.AddExtension(String fileExtension) at Microsoft.CSharp.CSharpCodeGenerator.FromSourceBatch(CompilerParameters options, String[] sources) at Microsoft.CSharp.CSharpCodeGenerator.System.CodeDom.Compiler.ICodeCompiler.CompileAssemblyFromSourceBatch(CompilerParameters options, String[] sources) at System.CodeDom.Compiler.CodeDomProvider.CompileAssemblyFromSource(CompilerParameters options, String[] sources) at System.Xml.Serialization.Compiler.Compile(Assembly parent, String ns, CompilerParameters parameters, Evidence evidence) at System.Xml.Serialization.TempAssembly.GenerateAssembly(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, Evidence evidence, CompilerParameters parameters, Assembly assembly, Hashtable assemblies) at System.Xml.Serialization.TempAssembly..ctor(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, String location, Evidence evidence) at System.Xml.Serialization.XmlSerializer.GenerateTempAssembly(XmlMapping xmlMapping, Type type, String defaultNamespace) at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace) at System.Xml.Serialization.XmlSerializer..ctor(Type type) at System.ServiceModel.Install.Configuration.ConfigurationHandlersInstallComponent..ctor(ConfigurationLoader configLoader) at System.ServiceModel.Install.Configuration.ConfigurationHandlersInstallComponent.CreateNativeConfigurationHandlersInstallComponent() at Microsoft.Tools.ServiceModel.ServiceModelReg.BuildActionQueue() at Microsoft.Tools.ServiceModel.ServiceModelReg.Run(String[] args) at Microsoft.Tools.ServiceModel.ServiceModelReg.TryRun(String[] args).
Possible root cause of the issue
These call stacks mean that the installation process is running into errors while attempting to create a new file in the temporary directory on the computer. In many of the cases I've seen of this type of error in the past, there was an anti-virus or anti-spyware program running on the system that was preventing applications from creating files in the temporary directory.
Options to work around the issue
When I see this type of error in the event logs during .NET Framework 3.0 or 3.5 installation, I typically suggest trying the following workaround:
If those steps are not helpful, then I typically suggest installing the .NET Framework 3.5 SP1 instead of the .NET Framework 3.0 or 3.5.
The reason I suggest this is because the .NET Framework 3.5 SP1 installs the .NET Framework 3.0 SP2 behind the scenes as a prerequisite. The underlying issue described above occurs while running a specific custom action during the .NET Framework 3.0 installation process. In the .NET Framework 3.0 SP2 installer, a logic change was made so that if this particular custom action fails, setup will continue to install instead of failing and rolling back. The end result is that if this action fails in 3.0 SP2, installation will complete and report success, but some specific functionality related to the System.ServiceModel namespace will not work correctly. However, if this scenario arises, it is possible to manually run the ServiceModelReg.exe tool after the fact if this functionality is needed and it isn't working as expected.
A few days ago, I wrote a blog post describing how to create an MSI-based installer for a Windows game created with XNA Game Studio 3.0. In that post, I described how to create custom actions in the MSI to check for system-level prerequisites (the .NET Framework and the XNA Framework Redistributable) and block the MSI from installing if they are not installed on the user's system.
Adding a bootstrapper to your game installer
The next thing I want to demonstrate is how to expand on the steps in that blog post to create an installer that will do all of the following:
Visual Studio includes a bootstrapper that can do all of the above, and XNA Game Studio 3.0 includes a prerequisite package for the XNA Framework Redistributable 3.0 that integrates with the Visual Studio bootstrapper. As a result, you can use the Visual Studio 2008 or Visual C# 2008 Express Edition IDE to create an installer for your game for both ClickOnce packages and VS setup/deployment projects, and I have previously posted steps for doing this in this blog post.
If you want to create an installer that uses the Visual Studio bootstrapper along with an MSI built with WiX instead of an MSI build with a VS setup/deployment project, you need to perform a couple of additional steps:
Updated sample that builds an MSI and a bootstrapper
I have posted a sample at http://cid-27e6a35d1a492af7.skydrive.live.com/self.aspx/Blog%7C_Tools/WiX%20Samples/wix%7C_sample%7C_xgs30%7C_game%7C_with%7C_bootstrapper.zip that extends my previous sample so you can use it to create a bootstrapper in addition to an MSI. It includes the following additions/changes from the previous sample:
How to use the updated sample
To try out this updated sample, you can use the following steps:
After doing the above, you can copy setup.exe and wix_sample_xgs30_game.msi from the Setup sub-directory to a server and run setup.exe to install your game and its prerequisites on another computer.
Advanced scenario - building an MSI with WiX by using a .wixproj file
Note - it is also possible to create an MSBuild project file to build an MSI using WiX instead of using a batch file that calls the WiX tools directly. I have included a sample MSBuild project file named wix_sample_xgs30_game.wixproj in the new sample that I have posted. It was created by using the Votive Visual Studio package that is a part of the WiX toolset, and it can be built by invoking msbuild.exe directly or by opening the .wixproj file in Visual Studio 2005 or 2008 standard edition or higher on a system that also has WiX v3.0 installed. Keep in mind that Votive is a Visual Studio add-in package, so it can only be installed for use in Visual Studio standard edition or higher. You cannot use it if you only have Visual C# 2008 Express Edition. You can create and build .wixproj files by hand outside of Visual Studio if you only have an Express Edition installed though.
<update date="3/21/2009"> Fixed broken link to sample installer files. </update>
Yesterday, the publishing process was completed for the online version of the XNA Game Studio 3.0 documentation. You can access this documentation at http://msdn.microsoft.com/library/bb200104.aspx.
The documentation is also installed as a part of the XNA Game Studio 3.0 product if you would like to use the documentation on your computer without requiring access to the Internet. You can launch the documentation by clicking on the Windows start menu, choosing All Programs, then XNA Game Studio 3.0, then XNA Game Studio Documentation.
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.
Earlier this week, we released updated versions of the existing XNA Game Studio starter kits on the Creators Club web site that will install and work with XNA Game Studio 3.0. To go along with that, we have also released some new and updated samples and a standalone version of the Spacewar starter kit for XNA Game Studio 3.0.
New XNA Game Studio 3.0 samples
We released 3 new samples that were created by Shawn Hargreaves to demonstrate new features in XNA Game Studio 3.0 and to better illustrate some of the best practices that will help you add polish to your games for publication on Xbox LIVE Community Games. Here is some info about the new samples:
Updated samples
We also released updated versions of the following samples to better illustrate community game best practices and new XNA Game Studio 3.0 features:
Spacewar starter kit
In addition, we released a standalone XNA Game Studio 3.0 version of the Spacewar starter kit (previously available as a part of the XNA Game Studio installation process, but replaced in 3.0 by the Platformer starter kit).
Converting existing 2.0 samples to 3.0
Most of the samples currently available on the Creators Club site are still only available in XNA Game Studio 2.0 versions. However, they can easily be converted to work with XNA Game Studio 3.0 (and you do not need to have VS 2005 or XNA Game Studio 2.0 installed to convert the samples). Here are steps you can use to accomplish this conversion:
There is also a set of unofficial XNA Game Studio 3.0 samples that a member of the community converted from 2.0 to 3.0 using steps like the ones listed above and then re-zipped and posted for download. You can find link to these unofficial samples in this blog post. This link mentions the XNA Game Studio 3.0 beta, but there are not any differences in the Visual Studio project file format between the beta and the final release of XNA Game Studio 3.0, so these samples should also work in the final release of 3.0 and not just in the beta.
<update date="11/15/2008"> Added a link to a download location for unofficial 3.0 versions of the XNA Game Studio samples. </update>
By default, when you create a new Windows game project in XNA Game Studio 3.0, it will target the .NET Framework 3.5. This means that the project will be configured to include references to assemblies that are a part of the .NET Framework 3.5 but are not a part of the .NET Framework 2.0 or 3.0, and it will also be configured to include the .NET Framework 3.5 as a prerequisite if you choose to deploy your game using ClickOnce or a Visual Studio setup/deployment project.
The Visual Studio 2008 product family includes a multi-targeting feature that allows you to select what version of the .NET Framework is required for your application. You can use multi-targeting to create a Windows game using XNA Game Studio 3.0 that only requires the .NET Framework 2.0 instead of 3.0 or 3.5. This can be particularly useful in reducing your installer footprint and to enable your game to run on Windows Vista after installing only the XNA Framework Redistributable Package 3.0 and your game payload.
There are some specific steps you need to take to change the project defaults and choose to target the .NET Framework 2.0 instead of 3.5 in your Windows game. The exact steps you need to take depend on what edition of Visual Studio 2008 you have installed.
Changing the .NET Framework version to target in Visual Studio 2008 Standard Edition or higher
In Visual Studio 2008 Standard Edition and higher, there is a drop-down menu in the New Project dialog that allows you to select the .NET Framework version you want to target in your XNA Game Studio 3.0 game project. The New Project dialog looks like the following, and the drop-down menu is in the top right corner of this dialog:
Note - if you change the drop-down menu from the default .NET Framework 3.5 value to .NET Framework 2.0, you will notice that the Content Pipeline Extension Library and Platformer Starter Kit project templates are no longer available. This is because those 2 project templates use features that rely on the .NET Framework 3.5.
After changing the .NET Framework version in the New Project dialog, you can create your project and it will automatically configure the assembly references, using statements in the source code and prerequisite packages for ClickOnce and setup projects appropriately.
Changing the .NET Framework version to target in Visual C# 2008 Express Edition
In Visual C# 2008 Express Edition, the New Project dialog does not offer an option to change the .NET Framework version you will target for your project. Instead, it will always create projects that target the .NET Framework 3.5. You can change the .NET Framework version you want to target after the project is created, but it requires some additional steps.
To change the .NET Framework version that your Windows game will target from the default .NET Framework 3.5 value to the .NET Framework 2.0, you can right-click on the project in the Visual Studio solution explorer and choose Properties. Then, in the Application properties tab, you can change the Target Framework drop-down. The UI for the Application properties tab looks like the following, and the Target Framework drop-down is directly below the Assembly Name text box:
Note - You can only target the .NET Framework 3.5 for Xbox 360 and Zune projects because there is only one available version of the .NET Compact Framework for these platforms in XNA Game Studio 3.0. As a result, the Target Framework drop-down will be blank and grayed out in the Application properties page for an Xbox 360 or Zune project.
After making a change to the Target Framework drop-down, you will be prompted to save, close and re-open the project. After clicking Yes to make the change, you will have to make the following additional manual changes to complete the .NET Framework re-targeting process:
You can also use the above steps in Visual Studio 2008 Standard Edition and higher to change the .NET Framework version that your game project will target if you do not want to keep the default value selected when you first created the project.
Possible deployment error messages related to .NET Framework version targeting issues
Skipping steps 1 and 2 above will result in build errors, so they will be easy to catch. Skipping step 3 can lead to more subtle error messages when users attempt to install your game.
There are a couple of installation errors your users might encounter that are related to .NET Framework version mismatches. If you create a game that targets the .NET Framework 3.0 or 3.5 and a user tries to run the ClickOnce .application file for the game on a system that has the .NET Framework 2.0 installed (but not 3.0 or 3.5), they will see an error with the following text:
Unable to install or run the application. The application requires that assembly WindowsBase Version 3.0.0.0 be installed in the Global Assembly Cache (GAC) first. Please contact your system administrator.
It looks like the following:
If you create a game that targets the .NET Framework 3.5 and a user tries to run the ClickOnce deployment manifest (named <game>.application) for the game on a system that has the .NET Framework 2.0 and 3.0 (but not 3.5) installed, they will see an error with the following text:
Unable to install or run the application. The application requires that assembly System.Core Version 3.5.0.0 be installed in the Global Assembly Cache (GAC) first. Please contact your system administrator.
In both of the above cases, you will need to double-check that you have selected the appropriate .NET Framework version as a prerequisite for your game. I have most commonly run into this type of error when creating a game that targets the .NET Framework 2.0 and then deciding to change it to target the .NET Framework 3.5 afterwards, but forgetting to update the deployment prerequisites for the game after making the targeting change (step 3 described above).
This type of error can also occur when creating a ClickOnce-based game installer, but then running the ClickOnce deployment manifest (<game>.application) instead of setup.exe on the system to deploy the game. The prerequisite checking logic for ClickOnce-based games exists in setup.exe, not in the deployment manifest. This can lead to cases where a user has a version of the .NET Framework (which will enable the .application file extension to be invoked), but do not have the version of the .NET Framework that your game is targeting.
A key thing to keep in mind here is that even if your Windows game does not use any .NET Framework 3.0 or 3.5 features, if you configure your project to target the .NET Framework 3.0 or 3.5, your users can run into this kind of error when they try to deploy your game to their system.
<update date="11/13/2008"> Added text that lists the deployment error messages to make this post easier to find in search engines. </update>
<update date="6/17/2009"> Fixed broken image links. </update>
Question:
I have an application that requires the .NET Framework 2.0 SP2. I would like to author a condition in my MSI-based installer to check for SP2 or higher so that the application will continue to install even if future service packs are released for the .NET Framework 2.0.
What is the correct way to check for the .NET Framework 2.0 SP2 or greater in an MSI-based installer?
Answer:
The following registry value that can be used to detect the service pack level for the .NET Framework 2.0 on a system:
[HKEY_LOCAL_MACHINE\Software\Microsoft\NET Framework Setup\NDP\v2.0.50727] SP
This SP registry value is a DWORD registry value. That means you can author a Windows Installer registry locator to retrieve the SP registry value during your application's installer. In WiX v3.0, the syntax looks like the following:
<Property Id="NETFRAMEWORK20_SP_LEVEL" Secure="yes"> <RegistrySearch Id="NetFramework20SP" Root="HKLM" Key="Software\Microsoft\NET Framework Setup\NDP\v2.0.50727" Name="SP" Type="raw" /> </Property>
Note - instead of defining the above registry locator in your setup authoring, you can include the following PropertyRef that is a part of the WixNetFxExtension in WiX v3.0:
<PropertyRef Id="NETFRAMEWORK20_SP_LEVEL" />
When querying this registry value in your MSI, the property value NETFRAMEWORK20_SP_LEVEL will be set to blank if the registry value does not exist on the system. It will be set to a value like "#0" or "#1" or "#2" depending on the exact service pack level on the system.
It will be tempting to simply check that NETFRAMEWORK20_SP_LEVEL >= "#2" in your MSI in order to verify that the .NET Framework 2.0 SP2 or later is installed. However, it is very important to note that this property value will be set to a string value and not a numerical value. That means that when you do comparisons in an MSI launch condition or custom action, you must use string comparison logic or else you run the risk of incorrectly detecting the service pack level on a user's system. The place where this causes the most problems is when the value goes from a single digit to double digits. For example, service pack 10 is greater than service pack 2, but the string value "#10" is less than the string value "#2". This means you cannot just check to make sure that the NETFRAMEWORK20_SP_LEVEL property is greater than or equal to "#2" to correctly detect service pack 2 or greater.
Because a string comparison can lead to incorrect results, I suggest that your MSI instead check individual values to narrow down the service pack level. For example, to verify that the system has service pack 2 or greater, you can make sure that the SP registry value exists and that it does not equal 0 or 1.
Implementing a launch condition to perform the check
The following WiX v3.0 syntax can be used to implement a Windows Installer launch condition that checks for the .NET Framework 2.0 SP2 or greater:
<PropertyRef Id="NETFRAMEWORK20_SP_LEVEL" /> <Condition Message="[ProductName] requires .NET Framework 2.0 SP2 or higher (Launch Condition). Setup will now exit."> <![CDATA[(NETFRAMEWORK20_SP_LEVEL AND NOT NETFRAMEWORK20_SP_LEVEL = "#0" AND NOT NETFRAMEWORK20_SP_LEVEL = "#1") OR Installed]]> </Condition>
<Condition Message="[ProductName] requires .NET Framework 2.0 SP2 or higher (Launch Condition). Setup will now exit."> <![CDATA[(NETFRAMEWORK20_SP_LEVEL AND NOT NETFRAMEWORK20_SP_LEVEL = "#0" AND NOT NETFRAMEWORK20_SP_LEVEL = "#1") OR Installed]]> </Condition>
Implementing a type 19 custom action to perform the check
When I create MSI-based installers, I prefer to use type 19 custom actions instead of launch conditions for this type of prerequisite checking because you can control the order in which the prerequisite checks are performed when using custom actions, but you cannot control the order when using launch conditions. The following WiX v3.0 syntax can be used to implement the same check for the .NET Framework 2.0 SP2 or greater as shown above with a custom action instead of a launch condition:
<PropertyRef Id="NETFRAMEWORK20_SP_LEVEL" /> <CustomAction Id="CA_CheckForNetfx20Sp2OrGreater" Error="[ProductName] requires .NET Framework 2.0 SP2 or higher. Setup will now exit." /> <InstallExecuteSequence> <LaunchConditions After="AppSearch"/> <Custom Action="CA_CheckForNetfx20Sp2OrGreater" After="LaunchConditions">(NOT NETFRAMEWORK20_SP_LEVEL OR NETFRAMEWORK20_SP_LEVEL = "#0" OR NETFRAMEWORK20_SP_LEVEL = "#1") AND NOT Installed</Custom> </InstallExecuteSequence> <InstallUISequence> <LaunchConditions After="AppSearch"/> <Custom Action="CA_CheckForNetfx20Sp2OrGreater" After="LaunchConditions">(NOT NETFRAMEWORK20_SP_LEVEL OR NETFRAMEWORK20_SP_LEVEL = "#0" OR NETFRAMEWORK20_SP_LEVEL = "#1") AND NOT Installed</Custom> </InstallUISequence>
<CustomAction Id="CA_CheckForNetfx20Sp2OrGreater" Error="[ProductName] requires .NET Framework 2.0 SP2 or higher. Setup will now exit." />
<InstallExecuteSequence> <LaunchConditions After="AppSearch"/> <Custom Action="CA_CheckForNetfx20Sp2OrGreater" After="LaunchConditions">(NOT NETFRAMEWORK20_SP_LEVEL OR NETFRAMEWORK20_SP_LEVEL = "#0" OR NETFRAMEWORK20_SP_LEVEL = "#1") AND NOT Installed</Custom> </InstallExecuteSequence>
<InstallUISequence> <LaunchConditions After="AppSearch"/> <Custom Action="CA_CheckForNetfx20Sp2OrGreater" After="LaunchConditions">(NOT NETFRAMEWORK20_SP_LEVEL OR NETFRAMEWORK20_SP_LEVEL = "#0" OR NETFRAMEWORK20_SP_LEVEL = "#1") AND NOT Installed</Custom> </InstallUISequence>
I have also created a full set of sample files that can be used to build an MSI using WiX v3.0 that includes a custom action to check for the .NET Framework 2.0 SP2 or later and block installation if it is not present. You can find the sample files at http://astebner.sts.winisp.net/Tools/WiX%20Samples/wix_sample_detect_netfx_sp_level.zip. To build this sample, you need to do the following:
Additional notes:
To go along with the recent release of the final version of XNA Game Studio 3.0, we have posted a set of updated starter kits that will work with Visual Studio 2008 or Visual C# 2008 Express Edition and XNA Game Studio 3.0.
You can find the download pages for the starter kits on the Creators Club site at http://creators.xna.com/en-US/education/starterkits/.
Each of the starter kits has download links for both XNA Game Studio 2.0 and XNA Game Studio 3.0 versions. When visiting the download pages, make sure that you download the MSIs that match the version of XNA Game Studio that you plan to use.
The installers for the 3.0 starter kits will check your system and verify that you have a Visual Studio 2008 edition installed and have XNA Game Studio 3.0 installed. After installing, the starter kits will appear in the My Templates section of Visual Studio new project dialog under the Visual C# node in the XNA Game Studio 3.0 sub-node.
If you are using ClickOnce to deploy a Windows game created with XNA Game Studio 3.0, you might run into an issue that causes the game to not run correctly after being deployed to a user's computer. If your Windows game project was originally created with the XNA Game Studio 3.0 CTP, it will not be configured to include the XNA Framework Redistributable 3.0 as a prerequisite by default (because that feature was not added until the 3.0 beta).
In this scenario, you can use the following steps to manually add the XNA Framework Redistributable 3.0 as a prerequisite for your Windows game, and then re-publish your game to get the changes to take effect:
New Windows game projects created in the final release of XNA Game Studio 3.0 will automatically include the XNA Framework Redistributable 3.0 as a prerequisite by default, so these steps are only needed for Windows game projects originally created with the 3.0 CTP.