Determining the Operating System Version Example

It's important to know which version of Windows an application is running on if you plan to use special operating system features. The GetVersionEx() function is easy enough to understand—it might seem as if you could use it directly within the .NET Framework application using PInvoke. However, it's not as easy you might think to make this function work.

There are several problems to overcome when using the GetVersionEx() function. The first is that the

GetVersionEx() function can use one of two data structures as input: OSVERSIONINFO and OSVERSIONINFOEX. The GetVersionEx() function was originally designed to use the OSVERSIONINFO data structure exclusively. In fact, using the OSVERSIONINFOEX data structure on an older version of Windows will fail, and it's one of the tests you need to run to determine which version of Windows the host system is using.

Note The .NET Framework provides the OperatingSystem class as part of the System namespace. It's possible to perform some level of version checking using the methods in this namespace, but it appears that the OperatingSystem class relies on the GetVersion() Win32 API call instead of the GetVersionEx() Win32 API call to obtain the version information. At the very least, the OperatingSystem class methods use the OSVERSIONINFO structure instead of the OSVERSIONINFOEX structure, which provides additional information. While you can use the OperatingSystem class methods in many situations, it's helpful to remember that the Win32 API call does provide additional information. The additional information includes service pack major and minor revision numbers, a suite mask, and product type. Consequently, you can use the OperatingSystem class methods to determine if you're using Windows XP or Windows .NET Server. However, you can't differentiate between the Home and Professional editions and Windows .NET Server—all three versions look the same.

The second problem is with the OSVERSIONINFOEX data structure. In the C/C++ header definition of the data structure, you'll find the normal DWORD values (among others). However, there's also a TCHAR array like the one shown here:

typedef struct _OSVERSIONINFOEX {

DWORD dwOSVersionInfoSize;

DWORD dwMajorVersion;

DWORD dwMinorVersion;

DWORD dwBuildNumber;

DWORD dwPlatformId;

TCHAR szCSDVersion[ 128 ];

WORD wServicePackMajor;

WORD wServicePackMinor;

WORD wSuiteMask;

BYTE wProductType;

BYTE wReserved; } OSVERSIONINFOEX, *POSVERSIONINFOEX, *LPOSVERSIONINFOEX;

The problem with this array is that it causes problems in the managed environment. You can define a data structure that contains a suitable replacement for the TCHAR, but when you try to create an instance of the data structure, C# will balk. You can only use it in unsafe mode. If you'll remember from previous discussions, Visual Basic won't even touch this data structure because it doesn't provide the means to work with unsafe code. Consequently, you'll need to create a wrapper DLL for this example. Listing 9.1 shows the wrapper DLL code. You'll find the Visual C++ source code for this part of the example in the \Chapter 09\OSVersion folder of the CD.

Listing 9.1: The OSVersion Wrapper DLL Performs a Simple Calling Mechanism

// This is the data structure that contains all of the available // operating system version information.

public _gc struct OSVERSIONINFOEX2

public:

Int32 dwOSVersionInfoSize; Int32 dwMajorVersion; Int32 dwMinorVersion;

Int32

dwBuildNumber;

Int32

dwPlatformId;

Char

szCSDVersion[];

Int16

wServicePackMajor;

Int16

wServicePackMinor;

Int16

wSuiteMask;

Byte

wProductType;

Byte

wReserved;

public _gc class OSVer

public:

static bool GetOSVersion(OSVERSIONINFOEX2** VerInfo) {

OSVERSIONINFOEX OSVer; // Unmanaged version info.

OSVERSIONINFOEX2* LocalVer; // Local version info.

// Set some memory aside.

memset(&OSVer, 0, sizeof(OSVERSIONINFOEX)); OSVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

// Get the version information.

if (!GetVersionEx((OSVERSIONINFO *)&OSVer)) {

// Return a failure value if unsuccessful. return false;

// Transfer the data to the input structure. LocalVer = new OSVERSIONINFOEX2(); LocalVer->dwBuildNumber = OSVer.dwBuildNumber; LocalVer->dwMajorVersion = OSVer.dwMajorVersion; LocalVer->dwMinorVersion = OSVer.dwMinorVersion; LocalVer->dwOSVersionInfoSize = OSVer.dwOSVersionInfoSize; LocalVer->dwPlatformId = OSVer.dwPlatformId; LocalVer->wProductType = OSVer.wProductType; LocalVer->wServicePackMajor = OSVer.wServicePackMajor; LocalVer->wServicePackMinor = OSVer.wServicePackMinor; LocalVer->wSuiteMask = OSVer.wSuiteMask;

// The char array requires special handling. The Platform // SDK documentation gives the char array a specific size // in this case.

LocalVer->szCSDVersion = new Char[128];

LocalVer->szCSDVersion[Counter] = OSVer.szCSDVersion[Counter];

// Transfer the data to the client. *VerInfo = LocalVer;

return true;

As you can see, the OSVERSIONINFOEX2 data structure is a direct match for the OSVERSIONINFOEX data structure. The only difference is in the use of managed data types. Because Visual C++ works equally well with managed and unmanaged code, using the OSVERSIONINFOEX2 data structure doesn't present any problems.

The call to GetVersionEx() isn't difficult. All we do is create an unmanaged OSVERSIONINFOEX data structure, set memory aside for it, and set the size of the data structure in the OSVer.dwOSVersionlnfoSize variable. Notice the use of a type cast to pass the data structure to GetVersionEx(). If the function fails, then we return false. Otherwise, the function proceeds to transfer the data to a local copy of the OSVERSIONINFOEX2 data structure.

Most of the data transfers as you might expect. The only problem area is the TCHAR array. We need to use a for loop to transfer the data one TCHAR at a time. Of course, you need to size the managed array first. Because the C/C++ header doesn't use a constant for this particular array, you can simply use a number to size it. The final step is to place a pointer to the local version of the data structure in the reference passed by the application. The wrapper DLL returns true in this case.

The client code is a little more complex because it has to do something with the data contained within the OSVERSIONINFOEX2 data structure. Listing 9.2 shows the code you'll need to determine the host operating system for a client machine. The same type of process could detect a server operating system. You'll find the source for this part of the example in the \Chapter 09\C#\CheckVersion and \Chapter 09\VB\CheckVersion folders of the CD.

Listing 9.2: The Client Application Detects the Operating System Type

// This enumeration determines the platform ID type.

public enum PlatformID {

VER_PLATFORM_WIN3 2s = 0,

VER_PLATFORM_WIN3 2_WINDOWS = 1,

VER_PLATFORM_WIN3 2_NT = 2

// This enumeration determines the main product type.

public enum ProductType {

VER_NT_WORKSTATION = 0x0000001,

VER_NT_DOMAIN_CONTROLLER = 0x0000002,

VER_NT_SERVER = 0x0000003

// This enumeration contains the suite flags.

public enum SuiteMask : uint {

VER_SERVER_NT =

VER_WORKSTATION_NT =

VER_SUITE_SMALLBUSINESS =

VER_SUITE_ENTERPRISE =

VER_SUITE_BACKOFFICE =

VER_SUITE_COMMUNICATIONS =

VER_SUITE_TERMINAL = VER_SUITE_SMALLBUSINESS_RESTRICTED =

VER_SUITE_EMBEDDEDNT =

VER_SUITE_DATACENTER =

VER_SUITE_SINGLEUSERTS =

VER_SUITE_PERSONAL =

VER_SUITE_BLADE =

private void btnTest_Click(object sender, System.EventArgs e) {

OSVERSIONINFOEX2 OSVerInfo; // Version information.

0x80000000, 0x40000000, 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400

// Initialize the data structure. OSVerInfo = new OSVERSIONINFOEX2();

// Determine the extended version information.

if (!OSVer.GetOSVersion(ref OSVerInfo)) {

// If not successful, the host system is using an older // version of Windows.

MessageBox.Show("Version older than Windows NT 4 SP6.", "Version Information", MessageBoxButtons.OK, MessageBoxIcon.Information);

return;

// See if this is a Windows 9x system, if (OSVerInfo.dwPlatformId !=

(Int3 2)PlatformID.VER_PLATFORM_WIN3 2_NT)

MessageBox.Show("Using Windows 9x.",

"Version Information",

MessageBoxButtons.OK,

MessageBoxIcon.Information);

return;

// Determine if this is a workstation or server.

if (OSVerInfo.wProductType == (Int32)ProductType.VER_NT_WORKSTATION) {

// Determine if this is Windows XP. if ((OSVerInfo.dwMajorVersion == 5) && (OSVerInfo.dwMinorVersion == 1))

// Determine if this is the Personal Edition, if (OSVerInfo.wSuiteMask ==

(Int3 2)SuiteMask.VER_SUITE_PERSONAL)

MessageBox.Show("Using Windows XP Personal.", "Version Information", MessageBoxButtons.OK, MessageBoxIcon.Information);

return;

// Must be the Professional Edition.

MessageBox.Show("Using Windows XP Professional.", "Version Information", MessageBoxButtons.OK, MessageBoxIcon.Information);

return;

// Check for Windows 2000 Professional, if ((OSVerInfo.dwMajorVersion == 5) &&

(OSVerInfo.dwMinorVersion == 0))

MessageBox.Show("Using Windows 2000 Professional.", "Version Information", MessageBoxButtons.OK, MessageBoxIcon.Information);

return;

// Must be Windows NT 4 Workstation.

MessageBox.Show("Using Windows NT 4 Workstation.", "Version Information", MessageBoxButtons.OK, MessageBoxIcon.Information);

return;

// It's probably a server, but it's a good idea to check // anyway.

else if (OSVerInfo.wProductType ==

(Int3 2)ProductType.VER_NT_SERVER || OSVerInfo.wProductType == (Int3 2)ProductType.VER_NT_DOMAIN_CONTROLLER)

MessageBox.Show("Using one of the server versions.", "Version Information", MessageBoxButtons.OK, MessageBoxIcon.Information);

return;

// We can't determine the Windows version type.

MessageBox.Show("Unable to determine Windows version.", "Version Information Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

This example relies on three enumerations. The PlatformID enumeration indicates the major platform: Win32s running on a Windows 3x system, Win32 running on a Windows 9x system, or a Windows NT/2000/XP system. The ProductType enumeration determines if the target system is a workstation, server, or domain controller. The GetVersionEx() function will actually differentiate between a regular server and a domain controller, which comes in handy for some types of applications. Finally, the SuiteMask enumeration contains a list of platform subtypes. For example, this is the enumeration that tells you if the host system is a Windows 2000 Small Business server or a Windows XP Personal system. You can even use this enumeration to detect embedded systems.

The client code begins by creating an OSVERSIONINFOEX2 data structure and instantiating it. It calls the GetOSVersion() function found in the wrapper DLL. A call failure doesn't indicate that the call actually failed—it indicates that the system is using a version of Windows older than Windows NT 4 Service Pack 6.

The code proceeds by using a series of checks to determine the platform type. For example, if the OSVerInfo.dwPlatformId field doesn't contain the platform ID for a Windows NT system, then we know that the system is using Windows 9x or Windows 3x. Because the .NET Framework won't run on Windows 3x, we know for certain that it's a Windows 9x system.

The next series of checks begins by verifying that the host system is a workstation rather than a server. The code then uses the version number to detect the specific version of Windows. For example, Windows XP is version 5.1, while Windows 2000 is version 5.0. You can find a complete list of version numbers in the Platform SDK documentation. The remaining check for a Windows XP system is to differentiate between the Home Edition and the Professional Edition. All the code needs to do is check the OSVerInfo.wSuiteMask field because a Home Edition system will have the VER_SUITE_PERSONAL value. The checks continue through the series of systems that I decided to check for this example.

Was this article helpful?

0 0

Post a comment