|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Services
Chapters
Feature Zones
|
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
IntroductionNow 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. BackgroundThe 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.0The .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:
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:
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.
.NET Framework v3.0The .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:
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:
Fortunately, determining the actual version number is much simpler and only requires reading one registry value:
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 Using the codeIn order to consolidate checking all of the various registry keys and help isolate changes for future versions of the .NET Framework, the
As you can see, all of these functions use either the /// <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 InterestThe 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 The 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 ConsiderationsI 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 History17-August-2007:
06-March-2007:
10-February-2007:
04-February-2007:
03-February-2007:
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||