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

Version Helper API for .NET

4.93/5 (12 votes)
7 Jan 2014CPOL4 min read 38.8K   462  
Version Helper API for .NET – checking version of Windows 8.1 or Windows Server 2012 R2 without Environment.OSVersion or application manifest

Introduction

With Windows 8.1 and Windows Server 2012 R2 behavior of Environment.OSVersion (http://msdn.microsoft.com/library/system.environment.osversion.aspx) property was changed. Without explicit declaration of support of Windows 8.1 in application manifest this property returns incorrect version number: 6.2 while the correct value should be 6.3. This issue can be solved with OSVersionHelper class proposed with this article that provides features of Version Helper functions that allow comparison of version of running OS with known values.

Background

This change in Environment.OSVersion property is caused by GetVersionEx (http://msdn.microsoft.com/library/windows/desktop/ms724451.aspx) function from Windows API. As described in in section “Manifestation” in MSDN article “Operating system version changes in Windows 8.1” (http://msdn.microsoft.com/library/windows/desktop/dn302074.aspx), without correct application manifest this function will return version 6.2 for Windows 8.1 and Windows Server 2012 R2. More other, within Windows SDK 8.1 (http://msdn.microsoft.com/windows/desktop/bg162891) GetVersionEx function is declared as deprecated. Building of C/C++ code cause a warning message C4996 (http://msdn.microsoft.com/library/ttcz0bys.aspx) for each use of this function. However, building of .NET code that use Environment.OSVersion property does not cause warnings. At the moment of writing of this article MSDN page for Environment.OSVersion does not sate that this property uses a deprecated function and that correct version number can be retrieved using application manifest. However, application manifest cannot be created for a future versions of Windows in advance. Therefore keeping of using of Environment.OSVersion property require rebuilding of an application with a new manifest after each new Windows release.

Some other authors already addressed this issue for C/C++. For example, Ehsan A Samani with his article “Part1: Overcoming Windows 8.1's deprecation of GetVersionEx and GetVersion APIs” (http://www.codeproject.com/Articles/678606/Part1-Overcoming-Windows-8-1s-deprecation-of-GetVe) described some drawbacks of deprecating of GetVersionEx function.

As a solution Windows SDK 8.1 provides Version Helper functions (http://msdn.microsoft.com/library/windows/desktop/dn424972.aspx) which, as states corresponding MSDN article, “supersede GetVersion and GetVersionEx”. This statement is not correct since Version Helper functions allow comparing of famous versions of Windows but, for example, does not provide abilities to compare a build number of the OS or to check if the OS is Windows Embedded. It is also not possible to check if running OS is not greater than specified one.

Another problem of documentation for Version Helper functions is incorrect implicit description of each function as a part of Kernel32.lib, ntdll.lib, Kernel32.dll or ntdll.dll. All these functions both defined and declared with a keyword __forceinline in “VersionHelpers.h” header file which is part of Windows SDK 8.1. This means that Version Helper functions are defined only for C/C++ code that includes “VersionHelpers.h” file. Also these functions will be placed in the resulting binaries in case if they would be invoked or if there would be pointers to these functions.

Using the Code

For .NET, Microsoft did not provide any interface for Version Helper functions. This is reasonable because these functions are not exported by Kernel32.dll or ntdll.dll and cannot be called using PInvoke. However, a static class OSVersionHelper that provides the same features as Version Helper functions was developed and provided with a source code for this article.

The class contains own implementation of Version Helper functions. It has a PInvoke prototypes of functions VerSetConditionMask (http://msdn.microsoft.com/library/windows/desktop/ms725493.aspx) and VerifyVersionInfo (http://msdn.microsoft.com/library/windows/desktop/ms725492.aspx). These two functions were enough to implement all Version Helper functions. The prototypes of these functions are the following:

C#
[DllImport("kernel32.dll")]
static extern ulong VerSetConditionMask(ulong dwlConditionMask,
   uint dwTypeBitMask, byte dwConditionMask);
[DllImport("kernel32.dll")]
static extern bool VerifyVersionInfo(
    [In] ref OsVersionInfoEx lpVersionInfo,
    uint dwTypeMask, ulong dwlConditionMask);

The first parameter of VerifyVersionInfo function is OsVersionInfoEx structure which is a .NET wrapper for OSVERSIONINFOEX (http://msdn.microsoft.com/library/windows/desktop/ms724833.aspx) structure defined in “Winnt.h”. This data structure is defined as follows:

C#
[StructLayout(LayoutKind.Sequential)]
struct OsVersionInfoEx
{
    public uint OSVersionInfoSize;
    public uint MajorVersion;
    public uint MinorVersion;
    public uint BuildNumber;
    public uint PlatformId;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string CSDVersion;
    public ushort ServicePackMajor;
    public ushort ServicePackMinor;
    public ushort SuiteMask;
    public byte ProductType;
    public byte Reserved;
}

A comparison of version of running OS was implemented with the following method:

C#
static bool IsWindowsVersionOrGreater(
    uint majorVersion, uint minorVersion, ushort servicePackMajor)
{
    OsVersionInfoEx osvi = new OsVersionInfoEx();
    osvi.OSVersionInfoSize = (uint)Marshal.SizeOf(osvi);
    osvi.MajorVersion = majorVersion;
    osvi.MinorVersion = minorVersion;
    osvi.ServicePackMajor = servicePackMajor;
    // These constants initialized with corresponding definitions in
    // winnt.h (part of Windows SDK)
    const uint VER_MINORVERSION = 0x0000001;
    const uint VER_MAJORVERSION = 0x0000002;
    const uint VER_SERVICEPACKMAJOR = 0x0000020;
    const byte VER_GREATER_EQUAL = 3;
   ulong versionOrGreaterMask = VerSetConditionMask(
       VerSetConditionMask(
           VerSetConditionMask(
               0, VER_MAJORVERSION, VER_GREATER_EQUAL),
           VER_MINORVERSION, VER_GREATER_EQUAL),
       VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
   uint versionOrGreaterTypeMask = VER_MAJORVERSION |
       VER_MINORVERSION | VER_SERVICEPACKMAJOR;
    return VerifyVersionInfo(ref osvi, versionOrGreaterTypeMask.Value,
        versionOrGreaterMask.Value);
}

Checking if running OS is a server was implemented with the following property:

C#
static bool IsWindowsServer
{
    get
    {
        // These constants initialized with corresponding
        // definitions in winnt.h (part of Windows SDK)
        const byte VER_NT_WORKSTATION = 0x0000001;
        const uint VER_PRODUCT_TYPE = 0x0000080;
        const byte VER_EQUAL = 1;
        OsVersionInfoEx osvi = new OsVersionInfoEx();
        osvi.OSVersionInfoSize = (uint)Marshal.SizeOf(osvi);
        osvi.ProductType = VER_NT_WORKSTATION;
        ulong dwlConditionMask = VerSetConditionMask(
            0, VER_PRODUCT_TYPE, VER_EQUAL);
        return !VerifyVersionInfo(
            ref osvi, VER_PRODUCT_TYPE, dwlConditionMask);
    }
}

A complete source code with exhaustive comments including example of usage is provided for this article (OSVersion.zip). The example includes invocation of Environment.OSVersion property just to print reference version of the running OS that required an application manifest. Note that members of OSVersionHelper class work correctly with Windows 8.1 or Windows Server 2012 R2 even without an application manifest.

Points of Interest

Prototypes of VerSetConditionMask and VerifyVersionInfo functions as well as .NET wrapper for OSVERSIONINFOEX structure can be found at www.pinvoke.net with the following pages:

In future releases OSVersionHelper class can be improved in order to retrieve a version of running OS with binary search and using different condition masks for VerifyVersionInfo function. Additionally some other features can be implemented, e.g., detecting of X64 architecture, or composing of a name of running operating system.

License

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