If only for now, this article is specific to 32-bit Windows (i386 or x86).

The Product Suite

Windows comes in an ever-increasing variety of editions. A long-standing identifier is the product suite, which is most familiar to Windows programmers as the wSuiteMask member of the OSVERSIONINFOEX structure that is filled by the user-mode function GetVersionEx. Kernel-mode programmers have easy access to the product suite through the very similar function RtlGetVersion. Both functions pick their 16-bit product suite from the 32-bit SuiteMask member at offset 0x02D0 of the KUSER_SHARED_DATA structure that the kernel makes addressable at 0xFFDF0000 in kernel mode and 0x7FFE0000 in user mode. The kernel’s own record of the product suite can be read more obscurely through ExVerifySuite.

Discovery

The product suite is a combination of bit flags. It is first determined from the following registry value:

Key: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ProductOptions
Value: ProductSuite
Type: REG_MULTI_SZ

From as far back as Windows 2000, the kernel allows 256 bytes for the data and parses it only if it has the expected type. The builds of Windows NT 4.0 that query this value get however much memory is needed but do not fuss over the data type. Each recognised string in the data corresponds to one bit in the suite mask:

String From Registry Data Suite Mask Applicable Versions
Small Business 0x0001 (VER_SUITE_SMALLBUSINESS) 4.0 from Windows NT 4.0 SP3, and higher
Enterprise 0x0002 (VER_SUITE_ENTERPRISE) 4.0 from Windows NT 4.0 SP3, and higher
BackOffice 0x0004 (VER_SUITE_BACKOFFICE) 4.0 from Windows NT 4.0 SP3, and higher
CommunicationServer 0x0008 (VER_SUITE_COMMUNICATIONS) 4.0 from Windows NT 4.0 SP3, and higher
Terminal Server 0x0010 (VER_SUITE_TERMINAL) 5.0 and higher
Small Business(Restricted) 0x0020 (VER_SUITE_SMALLBUSINESS_RESTRICTED) 4.0 from Windows NT 4.0 SP4, and higher
EmbeddedNT 0x0040 (VER_SUITE_EMBEDDEDNT) 4.0 from Windows NT 4.0 SP4, and higher
DataCenter 0x0080 (VER_SUITE_DATACENTER) 5.0 and higher
Personal 0x0200 (VER_SUITE_PERSONAL) 5.0 from Windows 2000 SP1, and higher
Blade 0x0400 (VER_SUITE_BLADE) 5.0 from Windows 2000 SP1, and higher
Embedded(Restricted) 0x0800 (VER_SUITE_EMBEDDED_RESTRICTED) 5.2 and higher
Security Appliance 0x1000 (VER_SUITE_SECURITY_APPLIANCE) 5.2 and higher
Storage Server 0x2000 (VER_SUITE_STORAGE_SERVER) 5.2 from Windows Server 2003 SP1, and higher
Compute Server 0x4000 (VER_SUITE_COMPUTE_SERVER) 5.2 from Windows Server 2003 SP1, and higher
WH Server 0x8000 (VER_SUITE_WH_SERVER) 5.2 from Windows Server 2003 SP2,
6.0 from Windows Vista SP1, and higher
PhoneNT 0x00010000 6.2 and higher

Strings that are not in the list are ignored. Note that VER_SUITE_SINGLEUSERTS (0x0100) is not learnt from the ProductSuite value.

Although Windows NT 4.0 SP3 has the first kernel that is known to read the ProductSuite value, its KERNEL32 function GetVersionEx supports only the OSVERSIONINFO structure not the OSVERSIONINFOEX that is needed for learning the suite mask. Support for learning the suite mask through GetVersionEx begins with Windows NT 4.0 SP4, as does the storage in the KUSER_SHARED_DATA.

Unsurprisingly, the possible product suites did eventually outgrow the 16-bit allowance in the OSVERSIONINFOEX structure. Less unsurprising is that the continuation to 32 bits seems to be undocumented. The only known way to retrieve it in user mode, short of looking directly in the KUSER_SHARED_DATA, is through the NTDLL function RtlGetVersion, and only then by supplying a further-extended OSVERSIONINFOEX to fill in. Give the size as 0x0124 (for the Unicode form), and the 32-bit suite mask is filled in at offset 0x011C.

License Value

The kernel interprets the ProductSuite value during phase 0 of initialisation. Starting with Windows Vista, however, the suite mask that is obtained this way is not entirely believed. If the license value Kernel-ProductInfo is present and has 4 bytes of REG_DWORD data, then the suite mask is reappraised (still in phase 0). The effective outcome is that the following are all cleared from the ProductSuite value:

and are instead selected from the Kernel-ProductInfo:

Kernel-ProductInfo Suite Applicable Versions
0x01 (PRODUCT_ULTIMATE)
none 6.0 and higher
0x02 (PRODUCT_HOME_BASIC) VER_SUITE_PERSONAL 6.0 and higher
0x03 (PRODUCT_HOME_PREMIUM) VER_SUITE_PERSONAL 6.0 and higher
0x04 (PRODUCT_ENTERPRISE) none 6.0 and higher
0x05 (PRODUCT_HOME_BASIC_N) VER_SUITE_PERSONAL 6.0 and higher
0x06 (PRODUCT_BUSINESS) none 6.0 and higher
0x07 (PRODUCT_STANDARD_SERVER) none 6.0 and higher
0x08 (PRODUCT_DATACENTER_SERVER) VER_SUITE_DATACENTER 6.0 and higher
0x09 (PRODUCT_SMALLBUSINESS_SERVER) VER_SUITE_SMALLBUSINESS 6.0 before Windows Vista SP1
VER_SUITE_SMALLBUSINESS
VER_SUITE_SMALLBUSINESS_RESTRICTED
6.0 from Windows Vista SP1, and higher
0x0A (PRODUCT_ENTERPRISE_SERVER) VER_SUITE_ENTERPRISE 6.0 and higher
0x0B (PRODUCT_STARTER) VER_SUITE_PERSONAL 6.0 and higher
0x0C (PRODUCT_DATACENTER_SERVER_CORE) VER_SUITE_DATACENTER 6.0 and higher
0x0D (PRODUCT_STANDARD_SERVER_CORE) none 6.0 and higher
0x0E (PRODUCT_ENTERPRISE_SERVER_CORE) VER_SUITE_ENTERPRISE 6.0 and higher
0x0F (PRODUCT_ENTERPRISE_SERVER_IA64) VER_SUITE_ENTERPRISE 6.0 and higher
0x10 (PRODUCT_BUSINESS_N) none 6.0 and higher
0x11 (PRODUCT_WEB_SERVER) VER_SUITE_BLADE 6.0 and higher
0x12 (PRODUCT_CLUSTER_SERVER) none 6.0 and higher
0x13 (PRODUCT_HOME_SERVER) none 6.0 before Windows Vista SP1
VER_SUITE_WH_SERVER 6.0 from Windows Vista SP1, and higher
0x14 (PRODUCT_STORAGE_EXPRESS_SERVER) VER_SUITE_STORAGE_SERVER 6.0 and higher
0x15 (PRODUCT_STORAGE_STANDARD_SERVER) VER_SUITE_STORAGE_SERVER 6.0 and higher
0x16 (PRODUCT_STORAGE_WORKGROUP_SERVER) VER_SUITE_STORAGE_SERVER 6.0 and higher
0x17 (PRODUCT_STORAGE_ENTERPRISE_SERVER) VER_SUITE_STORAGE_SERVER 6.0 and higher
0x18 (PRODUCT_SERVER_FOR_SMALLBUSINESS) none 6.0 before Windows Vista SP1
VER_SUITE_SMALLBUSINESS
VER_SUITE_SMALLBUSINESS_RESTRICTED
6.0 from Windows Vista SP1, and higher
0x19 (PRODUCT_SMALLBUSINESS_SERVER_PREMIUM) VER_SUITE_SMALLBUSINESS 6.0 before Windows Vista SP1
VER_SUITE_SMALLBUSINESS
VER_SUITE_SMALLBUSINESS_RESTRICTED
6.0 from Windows Vista SP1, and higher
0x1A (PRODUCT_HOME_PREMIUM_N) VER_SUITE_PERSONAL 6.0 from Windows Vista SP1, and higher
0x1B (PRODUCT_ENTERPRISE_N) none 6.0 from Windows Vista SP1, and higher
0x1C (PRODUCT_ULTIMATE_N) none 6.0 from Windows Vista SP1, and higher
0x1D (PRODUCT_WEB_SERVER_CORE) VER_SUITE_BLADE 6.0 from Windows Vista SP1, and higher
0x1E (PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT) none 6.0 from Windows Vista SP1, and higher
0x1F (PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY) none 6.0 from Windows Vista SP1, and higher
0x20 (PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING) none 6.0 from Windows Vista SP1, and higher
0x21 (PRODUCT_SERVER_FOUNDATION) VER_SUITE_SMALLBUSINESS
VER_SUITE_SMALLBUSINESS_RESTRICTED
6.0 from Windows Vista SP1
none 6.0 from Windows Vista SP2, and higher
0x22 (PRODUCT_HOME_PREMIUM_SERVER) VER_SUITE_WH_SERVER 6.0 from Windows Vista SP1, and higher
0x23 (PRODUCT_SERVER_FOR_SMALLBUSINESS_V) VER_SUITE_SMALLBUSINESS
VER_SUITE_SMALLBUSINESS_RESTRICTED
6.0 from Windows Vista SP1, and higher
0x24 (PRODUCT_STANDARD_SERVER_V) none 6.0 from Windows Vista SP1, and higher
0x25 (PRODUCT_DATACENTER_SERVER_V) VER_SUITE_DATACENTER 6.0 from Windows Vista SP1, and higher
0x26 (PRODUCT_ENTERPRISE_SERVER_V) VER_SUITE_ENTERPRISE 6.0 from Windows Vista SP1, and higher
0x27 (PRODUCT_DATACENTER_SERVER_CORE_V) VER_SUITE_DATACENTER 6.0 from Windows Vista SP1, and higher
0x28 (PRODUCT_STANDARD_SERVER_CORE_V) none 6.0 from Windows Vista SP1, and higher
0x29 (PRODUCT_ENTERPRISE_SERVER_CORE_V) VER_SUITE_ENTERPRISE 6.0 from Windows Vista SP1, and higher
0x2A (PRODUCT_HYPERV) none 6.0 from Windows Vista SP1, and higher
0x2B (PRODUCT_STORAGE_EXPRESS_SERVER_CORE) VER_SUITE_STORAGE_SERVER 6.1 and higher
0x2C (PRODUCT_STORAGE_STANDARD_SERVER_CORE) VER_SUITE_STORAGE_SERVER 6.1 and higher
0x2D (PRODUCT_STORAGE_WORKGROUP_SERVER_CORE) VER_SUITE_STORAGE_SERVER 6.1 and higher
0x2E (PRODUCT_STORAGE_ENTERPRISE_SERVER_CORE) VER_SUITE_STORAGE_SERVER 6.1 and higher
0x3B (PRODUCT_ESSENTIALBUSINESS_SERVER_MGMT) none 6.1 and higher
0x3C (PRODUCT_ESSENTIALBUSINESS_SERVER_ADDL) none 6.1 and higher
0x3D (PRODUCT_ESSENTIALBUSINESS_SERVER_MGMTSVC) none 6.1 and higher
0x3E (PRODUCT_ESSENTIALBUSINESS_SERVER_ADDLSVC) none 6.1 and higher
0x41 (PRODUCT_EMBEDDED) none 6.1 before Windows 7 SP1
VER_SUITE_EMBEDDEDNT 6.1 from Windows 7 SP1, and higher

Whether there have been practical implications from the cases where different builds of ostensibly the same product are assessed differently as suites is not known. The possibility must be at least suspected as having caused trouble, however. Some of the suite masks act as indicators of a lesser capability: it could be distressing to pick this up from a Service Pack upgrade.

Terminal Server

Recognition of Windows as a Terminal Server becomes an issue in phase 2 of the kernel’s initialisation. Though the defined bits for the product suite allow for VER_SUITE_TERMINAL (0x0010) among the ones that are recognised by the applicable service packs of Windows NT 4.0, the first support in a Windows version examined for this study is from Windows 2000. Broadly speaking, the kernel may in phase 2 interpret more registry values to override VER_SUITE_TERMINAL from the ProductSuite or to set VER_SUITE_SINGLEUSERTS (0x0100). The general trend, as Terminal Server has become an everyday Windows feature, has favoured setting both.

In version 5.0, if VER_SUITE_TERMINAL is set from the earlier registry evaluation, the kernel clears it in phase 2 unless the following registry value has non-zero data:

Key: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Terminal Server
Value: TSEnabled
Default: 0

In effect, to have the suite mask show a Terminal Server on Windows 2000 you must have both Terminal Server in the ProductSuite value and a non-zero TSEnabled. In versions 5.1 and 5.2, if TSEnabled has non-zero data, then the kernel forces VER_SUITE_TERMINAL to be set: you thus get a Terminal Server from either registry value. In version 6.0 and higher, the kernel simply sets VER_SUITE_TERMINAL regardless of registry values.

If the kernel thinks to set VER_SUITE_SINGLEUSERTS, the registry value that decides the matter is:

Key: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Terminal Server
Value: TSAppCompat
Default: 1 in 5.0 to 5.2;
0 in 6.0 and higher

The value is surely meant to have REG_DWORD data, and is documented as such (in the Microsoft Knowledge Base article Examining the Terminal Server Key in HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control), with 0 to specify a Remote Administration Server and 1 an Application Server. Zero, including if defaulted, means that only one interactive session is permitted at a time. Indicating this as a restriction of VER_SUITE_TERMINAL seems to be the point to VER_SUITE_SINGLEUSERTS. The algorithm varies with the versions, however, such that it looks to have been possible in versions 5.1 and 5.2 for VER_SUITE_TERMINAL to be set while VER_SUITE_SINGLEUSERTS remains clear only because the kernel did not evaluate it. Whether this has ever mattered in practice is not known.

In version 5.0, if VER_SUITE_TERMINAL is known from both ProductSuite and TSEnabled, then VER_SUITE_SINGLEUSERTS gets set too if TSAppCompat is zero. In versions 5.1 and 5.2, if VER_SUITE_TERMINAL is known from TSEnabled, then VER_SUITE_SINGLEUSERTS gets set too if TSAppCompat is zero. In version 6.0 and higher, the kernel sets VER_SUITE_TERMINAL unless TSAppCompat is non-zero.

Storage

This completes the identification of the product suite. The kernel saves it as the SuiteMask in the KUSER_SHARED_DATA and also writes it as multi-string data for the ProductSuite value. Of course, this will ordinarily mean writing the same data back to the registry as was read earlier. The REG_MULTI_SZ strings are always written in increasing order of the bit flags. Two of the defined flags are skipped: VER_SUITE_SINGLEUSERTS which is not learnt from this registry value; and VER_SUITE_WH_SERVER for reasons unknown. Failure to write the ProductSuite value is fatal to Windows, causing the bug check SYSTEM_LICENSE_VIOLATION (0x9A).