5,136,916 members and growing! (14,290 online)
Email Password   helpLost your password?
Languages » C# » General     Intermediate License: The Code Project Open License (CPOL)

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

By Scott Dorman

Explains how to use managed code to detect which .NET Framework versions and service packs are installed.
VB, C# 2.0, C#, Windows, .NET, .NET 2.0VS2005, VS, Dev

Posted: 3 Feb 2007
Updated: 30 Dec 2007
Views: 41,307
Announcements



Search    
Advanced Search
Sitemap
18 votes for this Article.
Popularity: 5.55 Rating: 4.42 out of 5
0 votes, 0.0%
1
1 vote, 5.6%
2
1 vote, 5.6%
3
3 votes, 16.7%
4
13 votes, 72.2%
5
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report This Article

Introduction

Now that the .NET Framework v3.0 has been released, developers are faced with the possibility of multiple 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. If you are interested in C++ code to do this, check out Aaron Stebner's blog posting titled "Sample code to detect .NET Framework 1.0 and 1.1 and service packs" [^] or 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. However, with each major release of the .NET Framework, this registry location has changed. As an added benefit, using the registry to determine if a version of the .NET Framework is installed also allows us to determine the Service Pack level. However, the .NET Framework v1.0 uses a different registry key for this information when it is installed on Windows Media Center of Windows XP Tablet edition.

.NET Framework v1.0

The .NET Framework v1.0 uses a string value located at the following registry key to indicate the build number:

HKLM\Software\Microsoft\.NETFramework\Policy\v1.0\3705

In order to determine the Service Pack level, it is necessary to know if the operating system is Windows Media Center or Windows XP Tablet Edition before looking in the registry. For Windows Media Center or Windows XP Tablet Edition, the following registry key should be used:

HKLM\Software\Microsoft\Active Setup\Installed Components\{FDC11A6F-17D1-48f9-9EA3-9051954BAA24}\Version

For any other operating system, use the following registry key:

HKLM\Software\Microsoft\Active Setup\Installed Components\{78705f0d-e8db-4b2d-8193-982bdda15ecd}\Version

This registry value at either of these keys should be of the format #,#,####,#. The #,#,#### portion of the string is the Framework version while the last # is the Service Pack level.

.NET Framework v1.1, v2.0, and v3.5 (Orcas)

Starting with the .NET Framework v1.1, Microsoft realized that the method of encoding the version and Service Pack level used in v1.0 was not the best solution, so they changed the way this information is stored in the registry. This method actually works for version 1.1, 2.0, and the January CTP of version 3.5 (Orcas). To determine if any of these Framework versions are installed, the following registry keys are used:

Framework Version Registry Key
.NET Framework v1.1 HKLM\Software\Microsoft\NET Framework Setup\NDP\v1.1.4322\Install
.NET Framework v2.0 HKLM\Software\Microsoft\NET Framework Setup\NDP\v2.0.50727\Install
.NET Framework v3.5 HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.5\Install

All of these values are a DWord value, so if it is present and set to 1, then that version of the Framework is installed.

Determining the Service Pack level is just as easy and uses the following registry keys:

Framework Version Registry Key
.NET Framework v1.1 HKLM\Software\Microsoft\NET Framework Setup\NDP\v1.1.4322\SP
.NET Framework v2.0 HKLM\Software\Microsoft\NET Framework Setup\NDP\v2.0.50727\SP
.NET Framework v3.5 HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.5\SP

In order to determine the exact version of the Framework, we need to use the version data encoded in the registry key path. For the .NET Framework v2.0 and v3.5, an additional registry value can be used to retrieve the build number.

Framework Version Registry Key
.NET Framework v2.0 HKLM\Software\Microsoft\NET Framework Setup\NDP\v2.0.50727\Increment
.NET Framework v3.5 HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.5\Version

.NET Framework v3.0

The .NET Framework v3.0 changes the registry keys used, most likely to accomodate the additional Foundation libraries that are part of this version of the Framework. In order to detect if the Framework and any of the Foundation libraries are installed, you need to use the following registry keys:

Framework Version Registry Key
Core HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\InstallSuccess
Windows Communication Foundation HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Communication Foundation\InstallSuccess
Windows Presentation Foundation HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Presentation Foundation\InstallSuccess
Windows Workflow Foundation HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Workflow Foundation\InstallSuccess

Since there have been no Service Packs released for v3.0 and there are currently no registry keys in place to indicate the Service Pack, this is a best guess and will need to be verified when a Service Pack is released, but the registry keys should be:

Framework Version Registry Key
Core HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\SP
Windows Communication Foundation HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Communication Foundation\SP
Windows Presentation Foundation HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Presentation Foundation\SP
Windows Workflow Foundation HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Workflow Foundation\SP

Fortunately, determining the actual version number is much simpler and only requires reading one registry value:

Framework Version Registry Key
Core HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\InstallSuccess
Windows Communication Foundation HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Communication Foundation\Version
Windows Presentation Foundation HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Presentation Foundation\Version
Windows Workflow Foundation HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\Windows Workflow Foundation\FileVersion

Determining this information for Windows CardSpace is not as straight-forward. The registry can still be used to determine if Windows CardSpace is installed:

HKLM\System\CurrentControlSet\Services\idsvc\ImagePath

In order to determine the actual version, we must interogate the file using the FileVersionInfo class and the value returned from the registry key.

Using the code

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:

  • 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)

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

/// <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 (Orcas)
  /// </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:

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));
}

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 boolean 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:

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.

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

Revision History

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 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)

About the Author

Scott Dorman


Mvp
Scott is a C# MVP and has been involved with computers in one way or another for as long as he can remember, but started professionally in 1993. After spending 6 years as a systems administrator, Scott branched out and started developing eCommerce store fronts. From there, he has worked on many different projects and has been working with .NET and C# since 2001.

He has worked at Fortune 500 companies and privately held start-ups focused on IT consulting where he gained experience in embedded systems design and software development to systems administration and database programming, and everything in between.

Although his primary focus right now is commercial software applications using Microsoft .NET technologies, he prefers building infrastructure components, reusable shared libraries and helping companies define, develop and automate process standards and guidelines.

Scott is currently working as a senior developer and architect on the Gulf coast of Florida, where he lives with his three cats.
Occupation: Software Developer (Senior)
Location: United States United States

Other popular C# articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 25 of 30 (Total in Forum: 30) (Refresh)FirstPrevNext
Subject  Author Date 
GeneralUpdates for .NET Framework 3.5 Beta 2memberScott Dorman17:30 17 Aug '07  
QuestionDetecting if .NET Framework is installed at allmembereagle_dev10:17 20 Feb '07  
AnswerRe: Detecting if .NET Framework is installed at allmemberScott Dorman14:27 20 Feb '07  
GeneralEnvironmentmemberEd.Poore10:36 10 Feb '07  
GeneralRe: EnvironmentmemberScott Dorman12:20 10 Feb '07  
GeneralWhat about Vista?memberPahan Menski5:20 9 Feb '07  
GeneralRe: What about Vista?mvpNishant Sivakumar5:29 9 Feb '07  
GeneralRe: What about Vista?memberScott Dorman5:51 9 Feb '07  
GeneralRe: What about Vista?mvpNishant Sivakumar5:54 9 Feb '07  
GeneralRe: What about Vista?memberPahan Menski5:58 9 Feb '07  
GeneralRe: What about Vista?memberScott Dorman6:03 9 Feb '07  
GeneralRe: What about Vista?memberScott Dorman5:47 9 Feb '07  
GeneralRe: What about Vista?memberPahan Menski6:00 9 Feb '07  
GeneralRe: What about Vista?memberScott Dorman6:06 9 Feb '07  
GeneralRe: What about Vista?memberPahan Menski6:11 9 Feb '07  
GeneralRe: What about Vista?memberPahan Menski7:11 9 Feb '07  
GeneralRe: What about Vista?memberScott Dorman7:26 10 Feb '07  
GeneralTwo commentsmemberDanielMoth0:48 4 Feb '07  
GeneralRe: Two commentsmemberScott Dorman3:32 4 Feb '07  
GeneralJust out of curiosity...supporterMarc Clifton15:28 3 Feb '07  
GeneralRe: Just out of curiosity...memberScott Dorman16:00 3 Feb '07  
General