Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

Using managed code to detect what .NET Framework versions and service packs are installed

4.85/5 (39 votes)
8 Sep 2008CPOL9 min read 9   3K  
Explains how to use managed code to detect which .NET Framework versions and service packs are installed.

Introduction

Developers are increasingly faced with the possibility of multiple .NET Framework versions being installed on the same machine. The .NET architecture was designed to allow multiple versions of a component to be installed and run at the same time on a single system, and this side-by-side deployment extends to the .NET Framework itself. By allowing multiple versions of the .NET Framework to be installed on the same computer, applications built with version 1.0 of the .NET Framework can run using the newer versions, or continue running with the earlier version. By default, client-side applications will use the version of the .NET Framework with which they were built, even when a newer version is available on the client.

There are situations, however, where it becomes important, or at least interesting, to know which versions of the .NET Framework are installed. Typically, this is done during installation, and the best way to do this is using unmanaged C++. Remember, you can only run managed code if a version of the .NET Framework is already installed, so this class is not guaranteed to work as part of an installation process. Aaron Stebner has a great set of blog posts [^] (including sample code) that shows how to do this in C++, and you can also check out this article [^].

While C++ may be the best way to do this, there are still times when this same functionality is needed in managed code. After searching both Google and CodeProject, I was unable to find a satisfactory solution in managed code. As a result, I decided to port Aaron's code to C#. This code uses Generics, so it is only compatible with version 2.0 or later of the .NET Framework.

Background

The correct way to determine if a specific version of the .NET Framework is installed is to look in the Registry. Aaron and a few readers of this article have pointed out that this solution, while the recommended way, is not 100% accurate in all situations. There are occasions when the Registry keys are orphaned, such as OS reinstalls. The solution presented by Aaron uses a technique by Junfeng Zhang [^] which loads mscoree.dll and uses some of its API functions to query for the presence of specific versions of the .NET Framework. The problem with this approach is that it only works from unmanaged code.

I took a slightly different approach, which more closely follows how the .NET Framework 3.5 setup checks for its prerequisites [^]. This approach looks for the presence of mscorwks.dll and, if found, verifies that the version number of the file is greater than or equal to the requested .NET Framework version, in addition to verifying that the Registry value is greater than or equal to the requested .NET Framework version. Consequently, if the file isn't found, I simply test the Registry value.

Using the code

Each major release of the .NET Framework has changed the Registry location and, in some cases, the keys and value names to look for in the Registry. In order to consolidate checking all of the various Registry keys and help isolate changes for future versions of the .NET Framework, the FrameworkVersionDetection class was created. This class exposes the following public methods and properties:

  • public static bool IsInstalled(FrameworkVersion frameworkVersion)
  • public static bool IsInstalled(WindowsFoundationLibrary foundationLibrary)
  • public static int GetServicePackLevel(FrameworkVersion frameworkVersion)
  • public static int GetServicePackLevel(WindowsFoundationLibrary foundationLibrary)
  • public static Version GetExactVersion(FrameworkVersion frameworkVersion)
  • public static Version GetExactVersion(WindowsFoundationLibrary foundationLibrary)
  • public static IEnumerable InstalledFrameworkVersions

As you can see, all of these functions use either the FrameworkVersion or the WindowsFoundationLibrary enumeration. These enumerations have the following definition:

C#
/// <summary>/// Specifies the .NET Framework versions
///// </summary>
public enum FrameworkVersion
{
  /// <summary>
  /// .NET Framework 1.0
  /// </summary>
  Fx10,

  /// <summary>
  /// .NET Framework 1.1
  /// </summary>
  Fx11,

  /// <summary>
  /// .NET Framework 2.0
  /// </summary>
  Fx20,

  /// <summary>
  /// .NET Framework 3.0
  /// </summary>
  Fx30,

  /// <summary>
  /// .NET Framework 3.5
  /// </summary>
  Fx35,
}

/// <summary>
/// Specifies the .NET 3.0 Windows Foundation Library
/// </summary>
public enum WindowsFoundationLibrary
{
  /// <summary>
  /// Windows Communication Foundation
  /// </summary>
  WCF,

  /// <summary>
  /// Windows Presentation Foundation
  /// </summary>
  WPF,

  /// <summary>
  /// Windows Workflow Foundation
  /// </summary>
  WF,

  /// <summary>
  /// Windows CardSpace
  /// </summary>
  CardSpace,
}

A complete example in C# looks like this:

C#
bool fx10Installed = FrameworkVersionDetection.IsInstalled(FrameworkVersion.Fx10);
bool fx11Installed = FrameworkVersionDetection.IsInstalled(FrameworkVersion.Fx11);
bool fx20Installed = FrameworkVersionDetection.IsInstalled(FrameworkVersion.Fx20);
bool fx30Installed = FrameworkVersionDetection.IsInstalled(FrameworkVersion.Fx30);
bool fx35Installed = FrameworkVersionDetection.IsInstalled(FrameworkVersion.Fx35);

Console.WriteLine(".NET Framework 1.0 installed? {0}", fx10Installed);
if (fx10Installed)
{
   Console.WriteLine(".NET Framework 1.0 Exact Version: {0}",
      FrameworkVersionDetection.GetExactVersion(FrameworkVersion.Fx10));
   Console.WriteLine(".NET Framework 1.0 Service Pack: {0}",
      FrameworkVersionDetection.GetServicePackLevel(FrameworkVersion.Fx10));
}
Console.WriteLine();

Console.WriteLine(".NET Framework 1.1 installed? {0}", fx11Installed);
if (fx11Installed)
{
   Console.WriteLine(".NET Framework 1.1 Exact Version: {0}",
     FrameworkVersionDetection.GetExactVersion(FrameworkVersion.Fx11));
   Console.WriteLine(".NET Framework 1.1 Service Pack: {0}",
     FrameworkVersionDetection.GetServicePackLevel(FrameworkVersion.Fx11));
}
Console.WriteLine();

Console.WriteLine(".NET Framework 2.0 installed? {0}", fx20Installed);
if (fx20Installed)
{
   Console.WriteLine(".NET Framework 2.0 Exact Version: {0}",
     FrameworkVersionDetection.GetExactVersion(FrameworkVersion.Fx20));
   Console.WriteLine(".NET Framework 2.0 Service Pack: {0}",
     FrameworkVersionDetection.GetServicePackLevel(FrameworkVersion.Fx20));
}
Console.WriteLine();

Console.WriteLine(".NET Framework 3.0 installed? {0}", fx30Installed);
if (fx30Installed)
{
   Console.WriteLine(".NET Framework 3.0 Exact Version: {0}",
     FrameworkVersionDetection.GetExactVersion(FrameworkVersion.Fx30));
   Console.WriteLine(".NET Framework 3.0 Service Pack: {0}",
     FrameworkVersionDetection.GetServicePackLevel(FrameworkVersion.Fx30));

   bool fx30PlusWCFInstalled = 
        FrameworkVersionDetection.IsInstalled(WindowsFoundationLibrary.WCF);
   bool fx30PlusWPFInstalled = 
        FrameworkVersionDetection.IsInstalled(WindowsFoundationLibrary.WPF);
   bool fx30PlusWFInstalled = 
        FrameworkVersionDetection.IsInstalled(WindowsFoundationLibrary.WF);
   bool fx30PlusCardSpacesInstalled =
      FrameworkVersionDetection.IsInstalled(WindowsFoundationLibrary.CardSpace);

   Console.WriteLine();

   Console.WriteLine("Windows Communication Foundation installed? {0}", 
                     fx30PlusWCFInstalled);
   if (fx30PlusWCFInstalled)
   {
     Console.WriteLine("Windows Communication Foundation Exact Version: {0}",
       FrameworkVersionDetection.GetExactVersion(WindowsFoundationLibrary.WCF));
   }
   Console.WriteLine();

   Console.WriteLine("Windows Presentation Foundation installed? {0}", 
                     fx30PlusWPFInstalled);
   if (fx30PlusWPFInstalled)
   {
     Console.WriteLine("Windows Presentation Foundation Exact Version: {0}",
       FrameworkVersionDetection.GetExactVersion(WindowsFoundationLibrary.WPF));
   }
   Console.WriteLine();

   Console.WriteLine("Windows Workflow Foundation installed? {0}", 
                     fx30PlusWFInstalled);
   if (fx30PlusWFInstalled)
   {
     Console.WriteLine("Windows Workflow Foundation Exact Version: {0}",
       FrameworkVersionDetection.GetExactVersion(WindowsFoundationLibrary.WF));
   }
   Console.WriteLine();

   Console.WriteLine("Windows CardSpaces installed? {0}", 
                     fx30PlusCardSpacesInstalled);
   if (fx30PlusCardSpacesInstalled)
   {
     Console.WriteLine("Windows CardSpaces Exact Version: {0}",
       FrameworkVersionDetection.GetExactVersion(WindowsFoundationLibrary.CardSpace));
   }
   Console.WriteLine();
}
Console.WriteLine();

Console.WriteLine(".NET Framework 3.5 installed? {0}", fx35Installed);
if (fx35Installed)
{
   Console.WriteLine(".NET Framework 3.5 Exact Version: {0}",
     FrameworkVersionDetection.GetExactVersion(FrameworkVersion.Fx35));
   Console.WriteLine(".NET Framework 3.5 Service Pack: {0}",
     FrameworkVersionDetection.GetServicePackLevel(FrameworkVersion.Fx35));
}
 
foreach (Version fxVersion in FrameworkVersionDetection.InstalledFrameworkVersions)
{
   Console.WriteLine(fxVersion .ToString());
}

Registry keys

The Registry keys used to determine if a particular version of the .NET Framework is installed are:

Framework VersionRegistry Key
1.0HKLM\Software\Microsoft\.NETFramework\Policy\v1.0\3705
1.1HKLM\Software\Microsoft\NET Framework Setup\NDP\v1.1.4322\Install
2.0HKLM\Software\Microsoft\NET Framework Setup\NDP\v2.0.50727\Install
3.0HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\InstallSuccess
3.5HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.5\Install

For the .NET Framework v1.0, this is a string value; all of the other versions use a DWORD value which, if present and set to 1, indicates that version of the Framework is installed. For .NET 3.0, it is important to also verify that .NET 2.0 is also installed, and for .NET 3.5, you should verify that both .NET 2.0 and 3.0 are also installed. In order to determine the Service Pack level, the following Registry keys are used:

Framework VersionRegistry Key

1.0

(Windows Media Center

or

Windows XP Tablet Edition)

HKLM\Software\Microsoft\Active Setup\Installed Components\{FDC11A6F-17D1-48f9-9EA3-9051954BAA24}\Version
1.0HKLM\Software\Microsoft\Active Setup\Installed Components\{78705f0d-e8db-4b2d-8193-982bdda15ecd}\Version
1.1HKLM\Software\Microsoft\NET Framework Setup\NDP\v1.1.4322\SP
2.0HKLM\Software\Microsoft\NET Framework Setup\NDP\v2.0.50727\SP
3.0HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\SP
3.5HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.5\SP

As you can see, in order to determine the Service Pack level for the .NET Framework v1.0, it is necessary to know if the Operating System is Windows Media Center or Windows XP Tablet Edition before looking in the Registry. To complicate things even more, for .NET 1.0, the Registry value at either of these keys is a string value of the format #,#,####,#. The last # is the Service Pack level. For all of the other versions, this is a DWORD value indicating the Service Pack level.

Finally, to determine the exact version number of the Framework, we look at the following Registry keys:

Framework VersionRegistry Key

1.0

(Windows Media Center

or

Windows XP Tablet Edition)

HKLM\Software\Microsoft\Active Setup\Installed Components\{FDC11A6F-17D1-48f9-9EA3-9051954BAA24}\Version
1.0HKLM\Software\Microsoft\Active Setup\Installed Components\{78705f0d-e8db-4b2d-8193-982bdda15ecd}\Version
1.1HKLM\Software\Microsoft\NET Framework Setup\NDP\v1.1.4322
2.0

HKLM\Software\Microsoft\NET Framework Setup\NDP\v2.0.50727\Version

- or -

HKLM\Software\Microsoft\NET Framework Setup\NDP\v2.0.50727\Increment

3.0HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Version
3.5HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.5\Version

Again, for .NET 1.0, it is necessary to know if the Operating System is Windows Media Center or Windows XP Tablet Edition before looking in the Registry, and the Registry value at either of these keys is a string value of the format #,#,####,#. The #,#,#### portion of the string is the Framework version.

For .NET 1.1, we use the name of the Registry key itself, which represents the version number, and for .NET 2.0, there are two different methods for determining the exact version number. If .NET 2.0 Original Release (RTM) is installed, then the version number is derived from the key name itself and the Increment Registry value (which is a string value representing the fourth part of the version number). Service Pack 1 for the .NET Framework 2.0 added the Version Registry value.

.NET Framework v3.0 Foundation libraries

In order to detect if any of the Foundation libraries are installed, you need to use the InstallSuccess Registry value from following rRegistry keys:

Foundation LibraryRegistry Key
Windows Communication FoundationHKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Communication Foundation
Windows Presentation FoundationHKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Presentation Foundation
Windows Workflow FoundationHKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Workflow Foundation

As is the case for the .NET Framework 3.0 itself, this is a DWORD value which, if present and set to 1, indicates that the Foundation library is installed. There have not been any service packs released specifically for the Foundation libraries, so at this time, there are no Registry keys to evaluate.

Determining the exact version number for any of the Foundation libraries uses the following Registry keys:

Foundation LibraryRegistry Key
Windows Communication FoundationHKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Communication Foundation\Version
Windows Presentation FoundationHKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Presentation Foundation\Version
Windows Workflow FoundationHKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Workflow Foundation\FileVersion

Determining this information for Windows CardSpace is not as straightforward. The Registry can still be used to determine if Windows CardSpace is installed:

HKLM\System\CurrentControlSet\Services\idsvc\ImagePath

In order to determine the exact version, we must interrogate the file using the FileVersionInfo class and the value returned from the Registry key.

Points of interest

The public methods are simply wrappers that determine which private function should be called. These private functions, in turn, query the appropriate Registry keys and process the result. However, the real work is done in the GetRegistryValue<T> function. This is a generic function that returns a bool value indicating if the requested Registry key was found and an out parameter that contains the value. By making this a generic function, I was able to simply encapsulate all of the Registry access in a single function, and was able to more closely match the original C++ code provided by Aaron. The GetRegistryValue<T> function is defined as:

C#
private static bool GetRegistryValue<T>(RegistryHive hive, string key, string value,
        RegistryValueKind kind, out T data)
{
  bool success = false;
  data = default(T);

  using (RegistryKey baseKey = RegistryKey.OpenRemoteBaseKey(hive, String.Empty))
  {
     if (baseKey != null)
     {
        using (RegistryKey registryKey = baseKey.OpenSubKey(key,
          RegistryKeyPermissionCheck.ReadSubTree))
        {
           if (registryKey != null)
           {
              // If the key was opened, try to retrieve the value.
              RegistryValueKind kindFound = registryKey.GetValueKind(value);
              if (kindFound == kind)
              {
                 object regValue = registryKey.GetValue(value, null);
                 if (regValue != null)
                 {
                    data = (T)Convert.ChangeType(regValue, typeof(T),
                      CultureInfo.InvariantCulture);
                    success = true;
                 }
              }
           }
        }
     }
  }
  return success;
}

It is important to note that if the user does not have the appropriate permissions to access the Registry, this function will throw an exception that will bubble up to the original caller. This was intentionally done to allow the caller the ability to take different actions based on the exception thrown.

Future considerations

I have not tested this code on any of the .NET Compact Framework versions, or determined how to detect the installed Compact Framework versions. If someone wants to do this investigation and let me know their findings, I will update the code accordingly.

If someone wants to test this on Windows XP 64-bit and Windows Vista 64-bit systems, let me know if it runs properly, and if not, what the errors are, and I will correct them.

Revision history

  • 8-September-2008:
    • Revised portions of the article to simplify the way the Registry key information is displayed.
    • Added a new InstalledFrameworkVersions property which returns an IEnumerable of Version objects representing the collection of installed frameworks.
    • Added some additional safety checks to help eliminate false positives from orphaned Registry keys.
    • Changed to using SandCastle for the documentation.
  • 17-August-2007:
    • Updated the Registry key for detecting the exact version of the .NET Framework v3.5 Beta 2.
  • 06-March-2007:
    • Corrected a few typographical errors.
  • 10-February-2007:
    • Corrected the Registry key path for the .NET 3.0 core keys.
    • Added a note asking for people to test this code on 64-bit XP and Vista and let me know what errors occur so I can correct them.
  • 04-February-2007:
    • Added information for the .NET Framework 3.5 (Orcas) January CTP.
    • Added "best guess" support for checking Service Pack level for the .NET Framework v3.0.
    • Added the GetExactVersion function; renamed IsFrameworkInstalled to IsInstalled, and added an overload to handle the Foundation libraries (WPF, WCF, WF, and CardSpace).
    • Added an overload to GetServicePackLevel to handle the Foundation libraries.
    • Added the WindowsFoundationLibrary enum.
    • Added Registry keys to show how to retrieve the version number information for the Framework.
    • Updated the sample code in the article.
    • Updated the formatting of the article.
    • Changed the article title and explanation to help clarify that this isn't intended to be run as part of an installer.
  • 03-February-2007:
    • Original article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)