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:
[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:
[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:
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;
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:
static bool IsWindowsServer
{
get
{
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.