Mailbag: How to detect the .NET Framework 2.0 SP2 or greater in an MSI-based installer

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>

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>

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 https://astebner.sts.winisp.net/Tools/WiX%20Samples/wix_sample_detect_netfx_sp_level.zip.  To build this sample, you need to do the following:

  1. Install the latest build of WiX v3.0 from the SourceForge releases page and choose to install it to the default location offered by the installer
  2. Download the sample .zip file from https://astebner.sts.winisp.net/Tools/WiX%20Samples/wix_sample_detect_netfx_sp_level.zip and extract the contents to your hard drive
  3. Run the file build_wix_sample_detect_netfx_sp_level.bat from the extracted .zip file in order to invoke the WiX v3.0 toolset to build the sample MSI

Additional notes:

  • The conditions listed above include the clause "OR Installed" (for the launch condition implementation) and "AND NOT Installed" (for the custom action implementation).  These clauses ensure that the user will not be blocked from running the uninstall process for this MSI if the condition is not met.  I recommend adding these clauses to any prerequisite checks that you include in your MSI that will block the user from running the MSI.  Without the clauses, the user would be forced to re-install the prerequisite package in order to uninstall your product, which can be a frustrating user experience.
  • The above logic explains how to implement a specific check for the .NET Framework 2.0 SP2 or later.  However, the same underlying principles apply to any type of prerequisite checking within a Windows Installer package that requires checking for a range of values that are retrieved from REG_DWORD values in the registry.  The key thing to remember is that a registry locator in an MSI will end up setting the property value to a string, so you need to keep in mind that you will need to account for string comparison logic and not the more simple numerical comparison logic when checking the property value in your MSI.