Determine Windows Version and Edition

Introduction

This article explains how to determine Windows version and edition. The method is very simple: The Windows API provides two functions, GetVersion/GetVersionEx, that fill the structure OSVERSIONINFO/OSVERSIONINFOEX with information such as major and minor version, product type, suite mask, platform, and so forth. This article, however, and the code provided here, does not list all the operating systems ever released by Microsoft, only the most usual ones.

The GetVersionEx() function from Platform SDK obtains extended information about the version of the operating system that is currently running. It uses an OSVERSIONINFO or OSVERSIONINFOEX (for Windows NT 4.0 SP6 and later) structure to retrieve the operating system version information.

The information in OSVERSIONINFO includes major and minor version numbers, a build number, a platform identifier, and descriptive text about the operating system.

The information in OSVERSIONINFOEX includes major and minor version numbers, a build number, a platform identifier, and information about product suites and the latest Service Pack installed on the system.

Note: GetVersionEx only returns correct information if the process does not run under a compatibility layer.

OS Versions

Before trying to retrieve the system version, I will explain how one can differentiate among different versions and editions.

The Win32 platform groups the Win95, Win98, and WinMe operating systems.

All of them have the Major Version equal to 4, and the difference is made by the Minor Version: 0 for Win95, 10 for Win98, and 90 for WinMe. Win95 also comes in an OSR2 release, and you must use the CSD Version to determine the actual edition. If it is 'C' or 'B', it is a OSR2 edition. For Win98, if the CSD Version is 'A', Second Edition is present.

The Windows network platform gathers WinNT, Win2000, WinXP, Win Server 2003, Win Vista, and Win Server 2008.

The first difference is made by the major version:

  • 3 is for Windows NT 3.5
  • 4 is for the Windows NT 4
  • 5 is for Windows 2000, Windows XP, Windows Server 2003 and Windows Home Server
  • 6 is for Windows Vista, Windows Server 2008 and Windows 7

The minor version is used, for instance, to differentiate among Windows 2000 (0), Windows XP(1), Windows Home Server (2) and Windows Server 2003 (2). Both Windows Vista and Windows Server 2008 have the same minor version, 0. Windows 7 and Windows Server 2008 R2 have the minor version 1.

The product type groups the operating systems in servers (3), workstations (2), and domain controllers (1).

Another flag that differentiate between editions is the suite mask. It can indicate an enterprise edition (0x0002), data center edition (0x0080), a personal/home edition (0x0200), or a web edition (0x0400).

A better, visual, grouping of the operating systems is shown in the following images.

For Windows NT:

For Windows 2000, XP, Server 2003 and Home Server:

For Windows Vista, Server 2008 and Windows 7:

In addition to that, on Windows Vista and Windows Server 2008 a new function is available in kernel32.dll, called GetProductInfo(). It retrieves the product type for the operating system on the local computer, and maps the type to the product types supported by the specified operating system. You have to supply the major and minor version of the OS and the major and minor version of the service pack; then it returns a value that can indicate an unknown product, and unlicensed product or a specific edition such as business, ultimate, enterprise, enterprise server, and so on. For more about the function and possible value, see MSDN.

Determining Windows Vista, Windows Server 2008 and Windows 7 edition based on the product type returned by GetProductInfo():

.

Determine Windows Version and Edition

Structure OSVERSIONINFOEX

This provides the following information:

  • dwOSVersionInfoSize: Size of this data structure, in bytes. Set this member to sizeof(OSVERSIONINFOEX) before calling the GetVersionEx or VerifyVersionInfo function.
  • dwMajorVersion: Major version number of the operating system.
  • dwMinorVersion: Minor version number of the operating system.
  • dwBuildNumber: Build number of the operating system.
  • dwPlatformId: Operating system platform.
  • szCSDVersion: Pointer to a null-terminated string, such as "Service Pack 3", that indicates the latest Service Pack installed on the system. If no Service Pack has been installed, the string is empty.
  • wServicePackMajor: Major version number of the latest Service Pack installed on the system. For example, for Service Pack 3, the major version number is 3. If no Service Pack has been installed, the value is zero.
  • wServicePackMinor: Minor version number of the latest Service Pack installed on the system. For example, for Service Pack 3, the minor version number is 0.
  • wSuiteMask: Bit flags that identify the product suites available on the system.
  • wProductType: Additional information about the system.
  • wReserved: Reserved for future use.

For more, check MSDN.

Structure SYSTEM_INFO

This contains information about the system, such as arhitecture and type of processors. Of particular interest is the wProcessorArchitecture attribute that specifies, as the name implies, the processor architecture. According to MSDN, it can be one of the following:

  • PROCESSOR_ARCHITECTURE_AMD64: for x64 AMND or Intel
  • PROCESSOR_ARCHITECTURE_IA64: for Intel Itanium Processor Family (IPF)
  • PROCESSOR_ARCHITECTURE_INTEL: for x86
  • PROCESSOR_ARCHITECTURE_UNKNOWN: if the processor is not known

However, winnt.h lists several more constants for the architecture:

#define PROCESSOR_ARCHITECTURE_INTEL            0
#define PROCESSOR_ARCHITECTURE_MIPS             1
#define PROCESSOR_ARCHITECTURE_ALPHA            2
#define PROCESSOR_ARCHITECTURE_PPC              3
#define PROCESSOR_ARCHITECTURE_SHX              4
#define PROCESSOR_ARCHITECTURE_ARM              5
#define PROCESSOR_ARCHITECTURE_IA64             6
#define PROCESSOR_ARCHITECTURE_ALPHA64          7
#define PROCESSOR_ARCHITECTURE_MSIL             8
#define PROCESSOR_ARCHITECTURE_AMD64            9
#define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64   10

For more, check MSDN.

GetProductInfo

This function is available only on Windows Vista and above. The WinNT.h header defines a list of possible product types:

#define PRODUCT_UNDEFINED                       0x00000000

#define PRODUCT_ULTIMATE                        0x00000001
#define PRODUCT_HOME_BASIC                      0x00000002
#define PRODUCT_HOME_PREMIUM                    0x00000003
#define PRODUCT_ENTERPRISE                      0x00000004
#define PRODUCT_HOME_BASIC_N                    0x00000005
#define PRODUCT_BUSINESS                        0x00000006
#define PRODUCT_STANDARD_SERVER                 0x00000007
#define PRODUCT_DATACENTER_SERVER               0x00000008
#define PRODUCT_SMALLBUSINESS_SERVER            0x00000009
#define PRODUCT_ENTERPRISE_SERVER               0x0000000A
#define PRODUCT_STARTER                         0x0000000B
#define PRODUCT_DATACENTER_SERVER_CORE          0x0000000C
#define PRODUCT_STANDARD_SERVER_CORE            0x0000000D
#define PRODUCT_ENTERPRISE_SERVER_CORE          0x0000000E
#define PRODUCT_ENTERPRISE_SERVER_IA64          0x0000000F
#define PRODUCT_BUSINESS_N                      0x00000010
#define PRODUCT_WEB_SERVER                      0x00000011
#define PRODUCT_CLUSTER_SERVER                  0x00000012
#define PRODUCT_HOME_SERVER                     0x00000013
#define PRODUCT_STORAGE_EXPRESS_SERVER          0x00000014
#define PRODUCT_STORAGE_STANDARD_SERVER         0x00000015
#define PRODUCT_STORAGE_WORKGROUP_SERVER        0x00000016
#define PRODUCT_STORAGE_ENTERPRISE_SERVER       0x00000017
#define PRODUCT_SERVER_FOR_SMALLBUSINESS        0x00000018
#define PRODUCT_SMALLBUSINESS_SERVER_PREMIUM    0x00000019

#define PRODUCT_UNLICENSED                      0xABCDABCD

In Windows SDK for Windows 7 and Server 2008, there are additional products defined:

#define PRODUCT_HOME_PREMIUM_N                      0x0000001A
#define PRODUCT_ENTERPRISE_N                        0x0000001B
#define PRODUCT_ULTIMATE_N                          0x0000001C
#define PRODUCT_WEB_SERVER_CORE                     0x0000001D
#define PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT    0x0000001E
#define PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY      0x0000001F
#define PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING     0x00000020
#define PRODUCT_SERVER_FOUNDATION                   0x00000021
#define PRODUCT_HOME_PREMIUM_SERVER                 0x00000022
#define PRODUCT_SERVER_FOR_SMALLBUSINESS_V          0x00000023
#define PRODUCT_STANDARD_SERVER_V                   0x00000024
#define PRODUCT_DATACENTER_SERVER_V                 0x00000025
#define PRODUCT_ENTERPRISE_SERVER_V                 0x00000026
#define PRODUCT_DATACENTER_SERVER_CORE_V            0x00000027
#define PRODUCT_STANDARD_SERVER_CORE_V              0x00000028
#define PRODUCT_ENTERPRISE_SERVER_CORE_V            0x00000029
#define PRODUCT_HYPERV                              0x0000002A
#define PRODUCT_STORAGE_EXPRESS_SERVER_CORE         0x0000002B
#define PRODUCT_STORAGE_STANDARD_SERVER_CORE        0x0000002C
#define PRODUCT_STORAGE_WORKGROUP_SERVER_CORE       0x0000002D
#define PRODUCT_STORAGE_ENTERPRISE_SERVER_CORE      0x0000002E
#define PRODUCT_STARTER_N                           0x0000002F
#define PRODUCT_PROFESSIONAL                        0x00000030
#define PRODUCT_PROFESSIONAL_N                      0x00000031
#define PRODUCT_SB_SOLUTION_SERVER                  0x00000032
#define PRODUCT_SERVER_FOR_SB_SOLUTIONS             0x00000033
#define PRODUCT_STANDARD_SERVER_SOLUTIONS           0x00000034
#define PRODUCT_STANDARD_SERVER_SOLUTIONS_CORE      0x00000035
#define PRODUCT_SB_SOLUTION_SERVER_EM               0x00000036
#define PRODUCT_SERVER_FOR_SB_SOLUTIONS_EM          0x00000037
#define PRODUCT_SOLUTION_EMBEDDEDSERVER             0x00000038
#define PRODUCT_SOLUTION_EMBEDDEDSERVER_CORE        0x00000039
#define PRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE   0x0000003F
#define PRODUCT_ESSENTIALBUSINESS_SERVER_MGMT       0x0000003B
#define PRODUCT_ESSENTIALBUSINESS_SERVER_ADDL       0x0000003C
#define PRODUCT_ESSENTIALBUSINESS_SERVER_MGMTSVC    0x0000003D
#define PRODUCT_ESSENTIALBUSINESS_SERVER_ADDLSVC    0x0000003E
#define PRODUCT_CLUSTER_SERVER_V                    0x00000040
#define PRODUCT_EMBEDDED                            0x00000041
#define PRODUCT_STARTER_E                           0x00000042
#define PRODUCT_HOME_BASIC_E                        0x00000043
#define PRODUCT_HOME_PREMIUM_E                      0x00000044
#define PRODUCT_PROFESSIONAL_E                      0x00000045
#define PRODUCT_ENTERPRISE_E                        0x00000046
#define PRODUCT_ULTIMATE_E                          0x00000047

For more info, check MSDN.

SystemInfo class

I've put together a class called SystemInfo that provides an easy way to retrieve the information about the operating system. Windows versions are defined in an enumeration in SystemInfo.h:

typedef enum WindowsVersion
{
   Windows,
   Windows32s,
   Windows95,
   Windows95OSR2,
   Windows98,
   Windows98SE,
   WindowsMillennium,
   WindowsNT351,
   WindowsNT40,
   WindowsNT40Server,
   Windows2000,
   WindowsXP,
   WindowsXPProfessionalx64,
   WindowsHomeServer,
   WindowsServer2003,
   WindowsServer2003R2,
   WindowsVista,
   WindowsServer2008,
   WindowsServer2008R2,
   Windows7,
};

Windows editions are defined in an additional enumeration called WindowsEdition.

typedef enum WindowsEdition
{
   EditionUnknown,

   Workstation,
   Server,
   AdvancedServer,
   Home,

   Ultimate,
   HomeBasic,
   HomePremium,
   Enterprise,
   HomeBasic_N,
   Business,
   StandardServer,
   DatacenterServer,
   SmallBusinessServer,
   EnterpriseServer,
   Starter,
   DatacenterServerCore,
   StandardServerCore,
   EnterpriseServerCore,
   EnterpriseServerIA64,
   Business_N,
   WebServer,
   ClusterServer,
   HomeServer,
   StorageExpressServer,
   StorageStandardServer,
   StorageWorkgroupServer,
   StorageEnterpriseServer,
   ServerForSmallBusiness,
   SmallBusinessServerPremium,
   HomePremium_N,
   Enterprise_N,
   Ultimate_N,
   WebServerCore,
   MediumBusinessServerManagement,
   MediumBusinessServerSecurity,
   MediumBusinessServerMessaging,
   ServerFoundation,
   HomePremiumServer,
   ServerForSmallBusiness_V,
   StandardServer_V,
   DatacenterServer_V,
   EnterpriseServer_V,
   DatacenterServerCore_V,
   StandardServerCore_V,
   EnterpriseServerCore_V,
   HyperV,
   StorageExpressServerCore,
   StorageStandardServerCore,
   StorageWorkgroupServerCore,
   StorageEnterpriseServerCore,
   Starter_N,
   Professional,
   Professional_N,
   SBSolutionServer,
   ServerForSBSolution,
   StandardServerSolutions,
   StandardServerSolutionsCore,
   SBSolutionServer_EM,
   ServerForSBSolution_EM,
   SolutionEmbeddedServer,
   SolutionEmbeddedServerCore,
   SmallBusinessServerPremiumCore,
   EssentialBusinessServerMGMT,
   EssentialBusinessServerADDL,
   EssentialBusinessServerMGMTSVC,
   EssentialBusinessServerADDLSVC,
   ClusterServer_V,
   Embedded,
   Starter_E,
   HomeBasic_E,
   HomePremium_E,
   Professional_E,
   Enterprise_E,
   Ultimate_E
};

The declaration for class SystemInfo is shown here:

class SystemInfo  
{
   WindowsVersion    m_nWinVersion;
   WindowsEdition    m_nWinEdition;
   TCHAR             m_szServicePack[128];
   OSVERSIONINFOEX   m_osvi;
   SYSTEM_INFO       m_SysInfo;
   BOOL              m_bOsVersionInfoEx;

private:
   void DetectWindowsVersion();
   void DetectWindowsEdition();
   void DetectWindowsServicePack();
   DWORD DetectProductInfo();

public:
   SystemInfo();
   virtual ~SystemInfo();

   WindowsVersion GetWindowsVersion() const;    // returns the windows version
   WindowsEdition GetWindowsEdition() const;    // returns the windows edition
   bool IsNTPlatform() const;                   // true if NT platform
   bool IsWindowsPlatform() const;              // true is Windows platform
   bool IsWin32sPlatform() const;               // true is Win32s platform
   DWORD GetMajorVersion() const;               // returns major version
   DWORD GetMinorVersion() const;               // returns minor version
   DWORD GetBuildNumber() const;                // returns build number
   DWORD GetPlatformID() const;                 // returns platform ID
   void GetServicePackInfo(TCHAR* szServicePack) const;// additional information about service pack
   bool Is32bitPlatform() const;                // true if platform is 32-bit
   bool Is64bitPlatform() const;                // true if platform is 64-bit
};

The GetServicePackInfo() method must be provided with a parameter that is not NULL. It does not allocate memory if the parameter is NULL.

All you have to do is declare an object of type SystemInfo. The constructor calls a private method that retrieves the system information.

Determine Windows Version and Edition

The constructor of the class initializes variables and calls the system API that returns information about the operating system.

SystemInfo::SystemInfo()
{
   BOOL canDetect = TRUE;
   PGetNativeSystemInfo pGNSI = NULL;

   m_bOsVersionInfoEx = FALSE;
   m_nWinVersion = Windows;
   m_nWinEdition = EditionUnknown;
   memset(m_szServicePack, 0, sizeof(m_szServicePack));

   // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
   ZeroMemory(&m_osvi, sizeof(OSVERSIONINFOEX));
   m_osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

   if( !(m_bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &m_osvi)) )
   {
      // If that fails, try using the OSVERSIONINFO structure.
      m_osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
      if (! GetVersionEx ( (OSVERSIONINFO *) &m_osvi) ) 
         canDetect = FALSE;
   }

   pGNSI = (PGetNativeSystemInfo) GetProcAddress(
      GetModuleHandle(_T("kernel32.dll")), 
      "GetNativeSystemInfo");

   if(NULL != pGNSI) pGNSI(&m_SysInfo);
   else GetSystemInfo(&m_SysInfo);

   if(canDetect)
   {
	   DetectWindowsVersion();
      DetectWindowsEdition();
      DetectWindowsServicePack();
   }
}

The implementation of method DetectWindowsVersion is shown below:

#define BUFSIZE 80
typedef void (WINAPI *GetNativeSysInfo)(LPSYSTEM_INFO);

void SystemInfo::DetectWindowsVersion()
{
   if(m_bOsVersionInfoEx)
   {
      switch (m_osvi.dwPlatformId)
      {
      case VER_PLATFORM_WIN32s:
         {
            m_nWinVersion = Windows32s;
         }
         break;

         // Test for the Windows 95 product family.
      case VER_PLATFORM_WIN32_WINDOWS:
         {
            switch(m_osvi.dwMajorVersion)
            {
            case 4:
               {
                  switch(m_osvi.dwMinorVersion)
                  {
                  case 0:
                     if (m_osvi.szCSDVersion[0] == 'B' || m_osvi.szCSDVersion[0] == 'C')
                        m_nWinVersion = Windows95OSR2;
                     else
                        m_nWinVersion = Windows95;
                     break;
                  case 10:
                     if (m_osvi.szCSDVersion[0] == 'A')
                        m_nWinVersion = Windows98SE;
                     else
                        m_nWinVersion = Windows98;
                     break;
                  case 90:
                     m_nWinVersion = WindowsMillennium;
                     break;       
                  }
               }
               break;
            }
         }
         break;

         // Test for the Windows NT product family.
      case VER_PLATFORM_WIN32_NT:
         {
            switch (m_osvi.dwMajorVersion)
            {
            case 3:
               m_nWinVersion = WindowsNT351;
               break;

            case 4:
               switch (m_osvi.wProductType)
               {
               case 1:
                  m_nWinVersion = WindowsNT40;
                  break;
               case 3:
                  m_nWinVersion = WindowsNT40Server;
                  break;
               }
               break;

            case 5:
               {
                  switch (m_osvi.dwMinorVersion)
                  {
                  case 0:
                     m_nWinVersion = Windows2000;
                     break;
                  case 1:
                     m_nWinVersion = WindowsXP;
                     break;
                  case 2:
                     {
                        if (m_osvi.wSuiteMask == VER_SUITE_WH_SERVER)
                        {
                           m_nWinVersion = WindowsHomeServer;
                        }
                        else if (m_osvi.wProductType == VER_NT_WORKSTATION &&
                           m_SysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
                        {
                           m_nWinVersion = WindowsXPProfessionalx64;
                        }
                        else
                        {
                           m_nWinVersion = ::GetSystemMetrics(SM_SERVERR2) == 0 ?
                           WindowsServer2003 :
                           WindowsServer2003R2;
                        }
                     }
                     break;
                  }

               }
               break;

            case 6:
               {
                  switch (m_osvi.dwMinorVersion)
                  {
                  case 0:
                     {
                        m_nWinVersion = m_osvi.wProductType == VER_NT_WORKSTATION ?
                        WindowsVista :
                        WindowsServer2008;
                     }
                     break;

                  case 1:
                     {
                        m_nWinVersion = m_osvi.wProductType == VER_NT_WORKSTATION ?
                        Windows7 :
                        WindowsServer2008R2;
                     }
                     break;
                  }
               }
               break;
            }
         }
         break;
      }
   }
   else // Test for specific product on Windows NT 4.0 SP5 and earlier
   {
      HKEY hKey;
      char szProductType[BUFSIZE];
      DWORD dwBufLen=BUFSIZE;
      LONG lRet;

      lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
         "SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
         0, KEY_QUERY_VALUE, &hKey );
      if( lRet != ERROR_SUCCESS )
         return;

      lRet = RegQueryValueEx( hKey, "ProductType", NULL, NULL,
         (LPBYTE) szProductType, &dwBufLen);
      if( (lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE) )
         return;

      RegCloseKey( hKey );

      if ( lstrcmpi( "WINNT", szProductType) == 0 )
      {
         if ( m_osvi.dwMajorVersion <= 4 )
         {
            m_nWinVersion = WindowsNT40;
            m_nWinEdition = Workstation;
         }
      }
      if ( lstrcmpi( "LANMANNT", szProductType) == 0 )
      {
         if ( m_osvi.dwMajorVersion == 5 && m_osvi.dwMinorVersion == 2 )
         {
            m_nWinVersion = WindowsServer2003;
         }

         if ( m_osvi.dwMajorVersion == 5 && m_osvi.dwMinorVersion == 0 )
         {
            m_nWinVersion = Windows2000;
            m_nWinEdition = Server;
         }

         if ( m_osvi.dwMajorVersion <= 4 )
         {
            m_nWinVersion = WindowsNT40;
            m_nWinEdition = Server;
         }
      }
      if ( lstrcmpi( "SERVERNT", szProductType) == 0 )
      {
         if ( m_osvi.dwMajorVersion == 5 && m_osvi.dwMinorVersion == 2 )
         {
            m_nWinVersion = WindowsServer2003;
            m_nWinEdition = EnterpriseServer;
         }

         if ( m_osvi.dwMajorVersion == 5 && m_osvi.dwMinorVersion == 0 )
         {
            m_nWinVersion = Windows2000;
            m_nWinEdition = AdvancedServer;
         }

         if ( m_osvi.dwMajorVersion <= 4 )
         {
            m_nWinVersion = WindowsNT40;
            m_nWinEdition = EnterpriseServer;
         }
      }
   }
}

Method DetectWindowsEdition() is shown bellow, but for the same of shortness some switch cases have been removed. You can find the entire core in the attached zip files.

void SystemInfo::DetectWindowsEdition()
{
   if(m_bOsVersionInfoEx)
   {
      switch(m_osvi.dwMajorVersion)
      {
      case 4:
         {
            switch(m_osvi.wProductType)
            {
            case VER_NT_WORKSTATION:
               m_nWinEdition = Workstation;
               break;

            case VER_NT_SERVER:
               m_nWinEdition = (m_osvi.wSuiteMask & VER_SUITE_ENTERPRISE) != 0 ? 
                  EnterpriseServer :
                  StandardServer;
               break;
            }
         }
         break;

      case 5:
         {
            switch (m_osvi.wProductType)
            {
            case VER_NT_WORKSTATION:
               {
                  m_nWinEdition = (m_osvi.wSuiteMask & VER_SUITE_PERSONAL) != 0 ?
                     Home :
                     Professional;
               }
               break;

            case VER_NT_SERVER:
               {
                  switch(m_osvi.dwMinorVersion)
                  {
                  case 0:
                     {
                        if ((m_osvi.wSuiteMask & VER_SUITE_DATACENTER) != 0)
                        {
                           m_nWinEdition = DatacenterServer;
                        }
                        else if ((m_osvi.wSuiteMask & VER_SUITE_ENTERPRISE) != 0)
                        {
                           m_nWinEdition = AdvancedServer;
                        }
                        else
                        {
                           m_nWinEdition = Server;
                        }
                     }
                     break;

                  default:
                     {
                        if ((m_osvi.wSuiteMask & VER_SUITE_DATACENTER) != 0)
                        {
                           m_nWinEdition = DatacenterServer;
                        }
                        else if ((m_osvi.wSuiteMask & VER_SUITE_ENTERPRISE) != 0)
                        {
                           m_nWinEdition = EnterpriseServer;
                        }
                        else if ((m_osvi.wSuiteMask & VER_SUITE_BLADE) != 0)
                        {
                           m_nWinEdition = WebServer;
                        }
                        else
                        {
                           m_nWinEdition = StandardServer;
                        }
                     }
                     break;
                  }
               }
               break;
            }
         }
         break;

      case 6:
         {
            DWORD dwReturnedProductType = DetectProductInfo();
            switch (dwReturnedProductType)
            {
            case PRODUCT_UNDEFINED:
               m_nWinEdition = EditionUnknown;
               break;

            case PRODUCT_ULTIMATE:
               m_nWinEdition = Ultimate;
               break;
            case PRODUCT_HOME_BASIC:
               m_nWinEdition = HomeBasic;
               break;
            case PRODUCT_HOME_PREMIUM:
               m_nWinEdition = HomePremium;
               break;
            case PRODUCT_ENTERPRISE:
               m_nWinEdition = Enterprise;
               break;
            case PRODUCT_HOME_BASIC_N:
               m_nWinEdition = HomeBasic_N;
               break;
            case PRODUCT_BUSINESS:
               m_nWinEdition = Business;
               break;
            case PRODUCT_STANDARD_SERVER:
               m_nWinEdition = StandardServer;
               break;
            case PRODUCT_DATACENTER_SERVER:
               m_nWinEdition = DatacenterServer;
               break;
            case PRODUCT_SMALLBUSINESS_SERVER:
               m_nWinEdition = SmallBusinessServer;
               break;
            case PRODUCT_ENTERPRISE_SERVER:
               m_nWinEdition = EnterpriseServer;
               break;
            case PRODUCT_STARTER:
               m_nWinEdition = Starter;
               break;
            case PRODUCT_DATACENTER_SERVER_CORE:
               m_nWinEdition = DatacenterServerCore;
               break;
            case PRODUCT_STANDARD_SERVER_CORE:
               m_nWinEdition = StandardServerCore;
               break;
            case PRODUCT_ENTERPRISE_SERVER_CORE:
               m_nWinEdition = EnterpriseServerCore;
               break;
            case PRODUCT_ENTERPRISE_SERVER_IA64:
               m_nWinEdition = EnterpriseServerIA64;
               break;
            case PRODUCT_BUSINESS_N:
               m_nWinEdition = Business_N;
               break;
            case PRODUCT_WEB_SERVER:
               m_nWinEdition = WebServer;
               break;
            case PRODUCT_CLUSTER_SERVER:
               m_nWinEdition = ClusterServer;
               break;
            case PRODUCT_HOME_SERVER:
               m_nWinEdition = HomeServer;
               break;
            case PRODUCT_STORAGE_EXPRESS_SERVER:
               m_nWinEdition = StorageExpressServer;
               break;
            case PRODUCT_STORAGE_STANDARD_SERVER:
               m_nWinEdition = StorageStandardServer;
               break;
            case PRODUCT_STORAGE_WORKGROUP_SERVER:
               m_nWinEdition = StorageWorkgroupServer;
               break;
            case PRODUCT_STORAGE_ENTERPRISE_SERVER:
               m_nWinEdition = StorageEnterpriseServer;
               break;
            case PRODUCT_SERVER_FOR_SMALLBUSINESS:
               m_nWinEdition = ServerForSmallBusiness;
               break;
            case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
               m_nWinEdition = SmallBusinessServerPremium;
               break;

#if _WIN32_WINNT >= 0x0601 // windows 7
            case PRODUCT_HOME_PREMIUM_N:
               m_nWinEdition = HomePremium_N;
               break;
            case PRODUCT_ENTERPRISE_N:
               m_nWinEdition = Enterprise_N;
               break;
            case PRODUCT_ULTIMATE_N:
               m_nWinEdition = Ultimate_N;
               break;
            /* more cases here... */
#endif
            }
         }
         break;
      }
   }
}

If the major version is 6, we can call system API GetProductInfo() to get additional information about the system.

DWORD SystemInfo::DetectProductInfo()
{
   DWORD dwProductInfo = PRODUCT_UNDEFINED;

#if _WIN32_WINNT >= 0x0600 
	if(m_osvi.dwMajorVersion >= 6)
	{
		PGetProductInfo lpProducInfo = (PGetProductInfo)GetProcAddress(
			GetModuleHandle(_T("kernel32.dll")), "GetProductInfo");

		if(NULL != lpProducInfo)
		{
			lpProducInfo(m_osvi.dwMajorVersion, 
							m_osvi.dwMinorVersion, 
							m_osvi.wServicePackMajor, 
							m_osvi.wServicePackMinor, 
							&dwProductInfo);
		}
	}
#endif

   return dwProductInfo;
}

Information about the service pack is retrieved in function DetectWindowsServicePack(). This is shown bellow:

void SystemInfo::DetectWindowsServicePack()
{
   // Display service pack (if any) and build number.

   if( m_osvi.dwMajorVersion == 4 && 
      lstrcmpi( m_osvi.szCSDVersion, "Service Pack 6" ) == 0 )
   {
      HKEY hKey;
      LONG lRet;

      // Test for SP6 versus SP6a.
      lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
         "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009",
         0, KEY_QUERY_VALUE, &hKey );
      if( lRet == ERROR_SUCCESS )
         sprintf(m_szServicePack, "Service Pack 6a (Build %d)\n", m_osvi.dwBuildNumber & 0xFFFF );         
      else // Windows NT 4.0 prior to SP6a
      {
         sprintf(m_szServicePack, "%s (Build %d)\n",
            m_osvi.szCSDVersion,
            m_osvi.dwBuildNumber & 0xFFFF);
      }

      RegCloseKey( hKey );
   }
   else // Windows NT 3.51 and earlier or Windows 2000 and later
   {
      sprintf(m_szServicePack, "%s (Build %d)\n",
         m_osvi.szCSDVersion,
         m_osvi.dwBuildNumber & 0xFFFF);
   }
}

Determine Windows Version and Edition

An example of SystemInfo usage is shown here:

int main(int argc, char* argv[])
{
	SystemInfo sysInfo;
	TCHAR szServicePack[128] = {0};
	
	switch(sysInfo.GetWindowsVersion())
	{
   case Windows:                 cout << "Windows" << endl; break;
   case Windows32s:              cout << "Windows 32s" << endl; break;
   case Windows95:               cout << "Windows 95" << endl; break;
   case Windows95OSR2:           cout << "Windows 95 SR2" << endl; break;
   case Windows98:               cout << "Windows 98" << endl; break;
   case Windows98SE:             cout << "Windows 98 SE" << endl; break;
   case WindowsMillennium:       cout << "Windows Me" << endl; break;
   case WindowsNT351:            cout << "Windows NT 3.51" << endl; break;
   case WindowsNT40:             cout << "Windows NT 4.0" << endl; break;
   case WindowsNT40Server:       cout << "Windows NT 4.0 Server" << endl; break;
   case Windows2000:             cout << "Windows 2000" << endl; break;
   case WindowsXP:               cout << "Windows XP" << endl; break;
   case WindowsXPProfessionalx64:cout << "Windows XP Professional x64" << endl; break;
   case WindowsHomeServer:       cout << "Windows Home Server" << endl; break;
   case WindowsServer2003:       cout << "Windows Server 2003" << endl; break;
   case WindowsServer2003R2:     cout << "Windows Server 2003 R2" << endl; break;
   case WindowsVista:            cout << "Windows Vista" << endl; break;
   case WindowsServer2008:       cout << "Windows Server 2008" << endl; break;
   case WindowsServer2008R2:     cout << "Windows Server 2008 R2" << endl; break;
   case Windows7:                cout << "Windows 7" << endl; break;
	}

   switch(sysInfo.GetWindowsEdition())
   {
   case EditionUnknown:    cout << "Edition unknown Edition" << endl; break;
   case Workstation:       cout << "Workstation Edition" << endl; break;
   case Server:            cout << "Server Edition" << endl; break;
   case AdvancedServer:    cout << "Advanced Server Edition" << endl; break;
   case Home:              cout << "Home Edition" << endl; break;
   case Ultimate:          cout << "Ultimate Edition" << endl; break;
   case HomeBasic:         cout << "Home Basic Edition" << endl; break;
   case HomePremium:       cout << "Home Premium Edition" << endl; break;
   case Enterprise:        cout << "Enterprise Edition" << endl; break;
   case HomeBasic_N:       cout << "Home Basic N Edition" << endl; break;
   case Business:          cout << "Business Edition" << endl; break;
   case StandardServer:    cout << "Standard Server Edition" << endl; break;
   case EnterpriseServerCore: cout << "Enterprise Server Core Edition" << endl; break;
   case EnterpriseServerIA64: cout << "Enterprise Server IA64 Edition" << endl; break;
   case Business_N:           cout << "Business N Edition" << endl; break;
   case WebServer:            cout << "Web Server Edition" << endl; break;
   case ClusterServer:        cout << "Cluster Server Edition" << endl; break;
   case HomeServer:           cout << "Home Server Edition" << endl; break;
   // etc. etc.
   }

	cout << "Platform type: ";
	if(sysInfo.IsNTPlatform()) cout << "NT" << endl;
	else if(sysInfo.IsWindowsPlatform()) cout << "Windows" << endl;
	else if(sysInfo.IsWin32sPlatform()) cout << "Win32s" << endl;
	else cout << "Unknown" << endl;

	cout << "Major version: " << sysInfo.GetMajorVersion() << endl;
	cout << "Minor version: " << sysInfo.GetMinorVersion() << endl;
	cout << "Build number: " << sysInfo.GetBuildNumber() << endl;

	sysInfo.GetServicePackInfo(szServicePack);
	cout << "Service Pack info: " << szServicePack << endl;

	cout << "32-bit platform: " << std::boolalpha << sysInfo.Is32bitPlatform() << endl;
	cout << "64-bit platform: " << std::boolalpha << sysInfo.Is64bitPlatform() << endl;

	return 0;
}

Running this program in Windows 7 x46 yields:

Windows 7
Enterprise Edition
Platform type: NT
Major version: 6
Minor version: 1
Build number: 7600
Service Pack info:  (Build 7600)

32-bit platform: false
64-bit platform: true

For Windows Server 2008 R2 x64:

Windows Server 2008 R2
Standard Server Edition
Platform type: NT
Major version: 6
Minor version: 1
Build number: 7600
Service Pack info:  (Build 7600)

32-bit platform: false
64-bit platform: true

Running Under a Compatibility Layer

I was explaining at the beginning of the article that GetVersionEx() returns correct information about the operating system only when the application does not run under a compatibility layer. When running the the code under Windows Vista, I get the following output:

Windows Vista
Platform type: NT
Major version: 6
Minor version: 0
Build number: 6000
Service Pack info:  (Build 6000)

32-bit platform: true
64-bit platform: false

If I set it to run in compatibility mode for Win XP with SP2, I get:

Windows XP
Platform type: NT
Major version: 5
Minor version: 1
Build number: 2600
Service Pack info: Service Pack 2 (Build 2600)

32-bit platform: true
64-bit platform: false

And, if I set Windows NT 4.0 SP5, I get:

Windows NT 4.0
Platform type: NT
Major version: 4
Minor version: 0
Build number: 1381
Service Pack info: Service Pack 5 (Build 1381)

32-bit platform: true
64-bit platform: false

Notes

This code was tested only on a limited number of Windows versions and editions (Windows 2000, Windows XP Home, Professional, SP1, SP2, SP3, Windows Vista Ultimate, Windows 7 Ultimate x86 and x64, Windows Server 2008 R2 x64). It is possible that some bugs in detecting the correct edition exists. The code is provided as is, with no guaranties.

Not all the editions of Windows (Vista, Server 2003, 2008, Windows 7) are detected with this code. For more information see the GetProductInfo function.

Updates

  • January 07, 2005: Non const methods from SystemInfo class were removed
  • July 05, 2007: Updated for Windows Vista and Windows Server 2008
  • May 15, 2009: Updated to support Windows 7 and Windows Server 2008 R2
  • March 12, 2010: Refactorying of code to separate the detection of windows server, edition and service pack


About the Author

Marius Bancila

Marius Bancila is a Microsoft MVP for VC++. He works as a software developer for a Norwegian-based company. He is mainly focused on building desktop applications with MFC and VC#. He keeps a blog at www.mariusbancila.ro/blog, focused on Windows programming. He is the co-founder of codexpert.ro, a community for Romanian C++/VC++ programmers.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds