[sys/info] Initial version.

This commit is contained in:
Jeroen van Rijn
2022-09-01 00:43:47 +02:00
parent 317db2758a
commit d5f94d73ad
8 changed files with 618 additions and 2 deletions

View File

@@ -0,0 +1,26 @@
//+build arm32, arm64
package sysinfo
// TODO: Set up an enum with the ARM equivalent of the above.
CPU_Feature :: enum u64 {}
cpu_features: Maybe(CPU_Feature)
cpu_name: Maybe(string)
@(init, private)
init_cpu_features :: proc "c" () {
}
@(private)
_cpu_name_buf: [72]u8
@(init, private)
init_cpu_name :: proc "c" () {
when ODIN_ARCH == .arm32 {
copy(_cpu_name_buf, "ARM")
cpu_name = string(_cpu_name_buf[:3])
} else {
copy(_cpu_name_buf, "ARM64")
cpu_name = string(_cpu_name_buf[:5])
}
}

View File

@@ -1,5 +1,5 @@
//+build i386, amd64
package simd_x86
package sysinfo
import "core:intrinsics"
@@ -9,7 +9,6 @@ cpuid :: intrinsics.x86_cpuid
// xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
xgetbv :: intrinsics.x86_xgetbv
CPU_Feature :: enum u64 {
aes, // AES hardware implementation (AES NI)
adx, // Multi-precision add-carry instruction extensions
@@ -34,6 +33,7 @@ CPU_Feature :: enum u64 {
CPU_Features :: distinct bit_set[CPU_Feature; u64]
cpu_features: Maybe(CPU_Features)
cpu_name: Maybe(string)
@(init, private)
init_cpu_features :: proc "c" () {
@@ -92,3 +92,31 @@ init_cpu_features :: proc "c" () {
cpu_features = set
}
@(private)
_cpu_name_buf: [72]u8
@(init, private)
init_cpu_name :: proc "c" () {
number_of_extended_ids, _, _, _ := cpuid(0x8000_0000, 0)
if number_of_extended_ids < 0x8000_0004 {
return
}
_buf := transmute(^[0x12]u32)&_cpu_name_buf
_buf[ 0], _buf[ 1], _buf[ 2], _buf[ 3] = cpuid(0x8000_0002, 0)
_buf[ 4], _buf[ 5], _buf[ 6], _buf[ 7] = cpuid(0x8000_0003, 0)
_buf[ 8], _buf[ 9], _buf[10], _buf[11] = cpuid(0x8000_0004, 0)
// Some CPUs like may include leading or trailing spaces. Trim them.
// e.g. ` Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz`
brand := string(_cpu_name_buf[:])
for len(brand) > 0 && brand[0] == 0 || brand[0] == ' ' {
brand = brand[1:]
}
for len(brand) > 0 && brand[len(brand) - 1] == 0 || brand[len(brand) - 1] == ' ' {
brand = brand[:len(brand) - 1]
}
cpu_name = brand
}

View File

@@ -0,0 +1,33 @@
// +build darwin
package sysinfo
import sys "core:sys/darwin"
import "core:intrinsics"
@(init, private)
init_os_version :: proc "c" () {
os_version = {}
}
@(init)
init_ram :: proc() {
// Retrieve RAM info using `sysinfo`
CTL_HW :: 6
HW_MEMSIZE :: 24
sysctls := []int{CTL_HW, HW_MEMSIZE}
mem_size: i64
if intrinsics.syscall(
uintptr(sys.System_Call_Number.sysctl),
uintptr(raw_data(sysctls)), uintptr(len(sysctls)),
uintptr(&mem_size), uintptr(size_of(mem_size))) == 0 {
return
}
ram.total_ram = int(mem_size)
}
@(private)
sysctl :: proc(leaf: int)

View File

@@ -0,0 +1,54 @@
// +build linux
package sysinfo
import "core:c"
import sys "core:sys/unix"
import "core:intrinsics"
// import "core:fmt"
@(init, private)
init_os_version :: proc "c" () {
os_version = {}
}
Sys_Info :: struct {
uptime: c.long, // Seconds since boot
loads: [3]c.long, // 1, 5, 15 minute load averages
totalram: c.ulong, // Total usable main memory size
freeram: c.ulong, // Available memory size
sharedram: c.ulong, // Amount of shared memory
bufferram: c.ulong, // Memory used by buffers
totalswap: c.ulong, // Total swap space size
freeswap: c.ulong, // Swap space still available
procs: c.ushort, // Number of current processes
totalhigh: c.ulong, // Total high memory size
freehigh: c.ulong, // Available high memory size
mem_unit: c.int, // Memory unit size in bytes
_padding: [20 - (2 * size_of(c.long)) - size_of(c.int)]u8,
}
get_sysinfo :: proc "c" () -> (res: Sys_Info, ok: bool) {
si: Sys_Info
err := intrinsics.syscall(sys.SYS_sysinfo, uintptr(rawptr(&si)))
if err != 0 {
// Unable to retrieve sysinfo
return {}, false
}
return si, true
}
@(init)
init_ram :: proc() {
// Retrieve RAM info using `sysinfo`
si, ok := get_sysinfo()
if !ok {
return
}
ram = RAM{
total_ram = int(si.totalram) * int(si.mem_unit),
free_ram = int(si.freeram) * int(si.mem_unit),
total_swap = int(si.totalswap) * int(si.mem_unit),
free_swap = int(si.freeswap) * int(si.mem_unit),
}
}

View File

@@ -0,0 +1,305 @@
// +build windows
package sysinfo
import sys "core:sys/windows"
import "core:intrinsics"
import "core:strings"
@(private)
version_string_buf: [1024]u8
@(init, private)
init_os_version :: proc () {
/*
NOTE(Jeroen):
`GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10.
`RtlGetVersion` will return the true version.
Rather than include the WinDDK, we ask the kernel directly.
`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release)
*/
osvi: sys.OSVERSIONINFOEXW
osvi.dwOSVersionInfoSize = size_of(osvi)
status := sys.RtlGetVersion(&osvi)
if status != 0 {
return
}
product_type: sys.Windows_Product_Type
sys.GetProductInfo(
osvi.dwMajorVersion, osvi.dwMinorVersion,
u32(osvi.wServicePackMajor), u32(osvi.wServicePackMinor),
&product_type,
)
os_version = {
platform = .Windows,
major = int(osvi.dwMajorVersion),
minor = int(osvi.dwMinorVersion),
}
os_version.build[0] = int(osvi.dwBuildNumber)
b := strings.builder_from_bytes(version_string_buf[:])
strings.write_string(&b, "Windows ")
switch osvi.dwMajorVersion {
case 10:
switch osvi.wProductType {
case 1: // VER_NT_WORKSTATION:
if osvi.dwBuildNumber < 22000 {
strings.write_string(&b, "10 ")
} else {
strings.write_string(&b, "11 ")
}
format_windows_product_type(&b, product_type)
case: // Server or Domain Controller
switch osvi.dwBuildNumber {
case 14393:
strings.write_string(&b, "2016 Server")
case 17763:
strings.write_string(&b, "2019 Server")
case 20348:
strings.write_string(&b, "2022 Server")
case:
strings.write_string(&b, "Unknown Server")
}
}
case 6:
switch osvi.dwMinorVersion {
case 0:
switch osvi.wProductType {
case 1: // VER_NT_WORKSTATION
strings.write_string(&b, "Windows Vista ")
format_windows_product_type(&b, product_type)
case 3:
strings.write_string(&b, "Windows Server 2008")
}
case 1:
switch osvi.wProductType {
case 1: // VER_NT_WORKSTATION:
strings.write_string(&b, "Windows 7 ")
format_windows_product_type(&b, product_type)
case 3:
strings.write_string(&b, "Windows Server 2008 R2")
}
case 2:
switch osvi.wProductType {
case 1: // VER_NT_WORKSTATION:
strings.write_string(&b, "Windows 8 ")
format_windows_product_type(&b, product_type)
case 3:
strings.write_string(&b, "Windows Server 2012")
}
case 3:
switch osvi.wProductType {
case 1: // VER_NT_WORKSTATION:
strings.write_string(&b, "Windows 8.1 ")
format_windows_product_type(&b, product_type)
case 3:
strings.write_string(&b, "Windows Server 2012 R2")
}
}
case 5:
switch osvi.dwMinorVersion {
case 0:
strings.write_string(&b, "Windows 2000")
case 1:
strings.write_string(&b, "Windows XP")
case 2:
strings.write_string(&b, "Windows Server 2003")
}
}
// Grab DisplayVersion
os_version.version = format_display_version(&b)
// Grab build number and UBR
os_version.build[1] = format_build_number(&b, int(osvi.dwBuildNumber))
// Finish the string
os_version.as_string = strings.to_string(b)
format_windows_product_type :: proc (b: ^strings.Builder, prod_type: sys.Windows_Product_Type) {
#partial switch prod_type {
case .ULTIMATE:
strings.write_string(b, "Ultimate")
case .HOME_BASIC:
strings.write_string(b, "Home Basic")
case .HOME_PREMIUM:
strings.write_string(b, "Home Premium")
case .ENTERPRISE:
strings.write_string(b, "Enterprise")
case .CORE:
strings.write_string(b, "Home Basic")
case .HOME_BASIC_N:
strings.write_string(b, "Home Basic N")
case .EDUCATION:
strings.write_string(b, "Education")
case .EDUCATION_N:
strings.write_string(b, "Education N")
case .BUSINESS:
strings.write_string(b, "Business")
case .STANDARD_SERVER:
strings.write_string(b, "Standard Server")
case .DATACENTER_SERVER:
strings.write_string(b, "Datacenter")
case .SMALLBUSINESS_SERVER:
strings.write_string(b, "Windows Small Business Server")
case .ENTERPRISE_SERVER:
strings.write_string(b, "Enterprise Server")
case .STARTER:
strings.write_string(b, "Starter")
case .DATACENTER_SERVER_CORE:
strings.write_string(b, "Datacenter Server Core")
case .STANDARD_SERVER_CORE:
strings.write_string(b, "Server Standard Core")
case .ENTERPRISE_SERVER_CORE:
strings.write_string(b, "Enterprise Server Core")
case .BUSINESS_N:
strings.write_string(b, "Business N")
case .HOME_SERVER:
strings.write_string(b, "Home Server")
case .SERVER_FOR_SMALLBUSINESS:
strings.write_string(b, "Windows Server 2008 for Windows Essential Server Solutions")
case .SMALLBUSINESS_SERVER_PREMIUM:
strings.write_string(b, "Small Business Server Premium")
case .HOME_PREMIUM_N:
strings.write_string(b, "Home Premium N")
case .ENTERPRISE_N:
strings.write_string(b, "Enterprise N")
case .ULTIMATE_N:
strings.write_string(b, "Ultimate N")
case .HYPERV:
strings.write_string(b, "HyperV")
case .STARTER_N:
strings.write_string(b, "Starter N")
case .PROFESSIONAL:
strings.write_string(b, "Professional")
case .PROFESSIONAL_N:
strings.write_string(b, "Professional N")
case:
strings.write_string(b, "Unknown Edition")
}
}
// Grab Windows DisplayVersion (like 20H02)
format_display_version :: proc (b: ^strings.Builder) -> (version: string) {
value_type: ^sys.DWORD
display_version: [256]u16le
value_size := sys.DWORD(size_of(display_version))
status := sys.RegGetValueW(
sys.HKEY_LOCAL_MACHINE,
sys.L("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
sys.L("DisplayVersion"),
sys.RRF_RT_REG_SZ,
value_type,
raw_data(display_version[:]),
&value_size,
)
if status != 0 {
// Couldn't retrieve DisplayVersion
return
}
strings.write_string(b, " (version: ")
l := strings.builder_len(b^)
for r, i in display_version {
if r == 0 {
s := strings.to_string(b^)
version = string(s[l:][:i])
break
}
if r < 256 {
strings.write_rune(b, rune(r))
}
}
strings.write_rune(b, ')')
return
}
// Grab build number and UBR
format_build_number :: proc (b: ^strings.Builder, major_build: int) -> (ubr: int) {
value_type: ^sys.DWORD
_ubr: sys.DWORD
value_size := sys.DWORD(size_of(ubr))
status := sys.RegGetValueW(
sys.HKEY_LOCAL_MACHINE,
sys.L("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
sys.L("UBR"),
sys.RRF_RT_REG_DWORD,
value_type,
&_ubr,
&value_size,
)
if status != 0 {
// Couldn't retrieve DisplayVersion
return
}
ubr = int(_ubr)
strings.write_string(b, ", build: ")
strings.write_int(b, major_build)
strings.write_rune(b, '.')
strings.write_int(b, ubr)
return
}
}
@(init)
init_ram :: proc() {
state: sys.MEMORYSTATUSEX
state.dwLength = size_of(state)
ok := sys.GlobalMemoryStatusEx(&state)
if !ok {
return
}
ram = RAM{
total_ram = int(state.ullTotalPhys),
free_ram = int(state.ullAvailPhys),
total_swap = int(state.ullTotalPageFil),
free_swap = int(state.ullAvailPageFil),
}
}

View File

@@ -0,0 +1,38 @@
package sysinfo
when !(ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 || ODIN_ARCH == .arm32 || ODIN_ARCH == .arm64) {
#assert(false, "This package is unsupported on this architecture.")
}
os_version: OS_Version
ram: RAM
OS_Version_Platform :: enum {
Unknown,
Windows,
Linux,
MacOS,
iOS,
FreeBSD,
OpenBSD,
NetBSD,
}
OS_Version :: struct {
platform: OS_Version_Platform,
major: int,
minor: int,
patch: int,
build: [2]int,
version: string,
as_string: string,
}
RAM :: struct {
total_ram: int,
free_ram: int,
total_swap: int,
free_swap: int,
}

View File

@@ -637,6 +637,13 @@ foreign kernel32 {
) -> BOOL ---
}
@(default_calling_convention="stdcall")
foreign kernel32 {
GlobalMemoryStatusEx :: proc(
lpBuffer: ^MEMORYSTATUSEX,
) -> BOOL ---
}
PBAD_MEMORY_CALLBACK_ROUTINE :: #type proc "stdcall" ()
@(default_calling_convention="stdcall")
@@ -794,3 +801,14 @@ Control_Event :: enum DWORD {
logoff = 5,
shutdown = 6,
}
@(default_calling_convention="stdcall")
foreign kernel32 {
GetProductInfo :: proc(
OSMajorVersion: DWORD,
OSMinorVersion: DWORD,
SpMajorVersion: DWORD,
SpMinorVersion: DWORD,
product_type: ^Windows_Product_Type,
) -> BOOL ---
}

View File

@@ -17,6 +17,7 @@ size_t :: c.size_t
wchar_t :: c.wchar_t
DWORD :: c_ulong
DWORDLONG :: c.ulonglong
QWORD :: c.ulonglong
HANDLE :: distinct LPVOID
HINSTANCE :: HANDLE
@@ -3265,3 +3266,116 @@ IFileSaveDialogVtbl :: struct {
GetProperties: proc "stdcall" (this: ^IFileSaveDialog, ppStore: ^^IPropertyStore) -> HRESULT,
ApplyProperties: proc "stdcall" (this: ^IFileSaveDialog, psi: ^IShellItem, pStore: ^IPropertyStore, hwnd: HWND, pSink: ^IFileOperationProgressSink) -> HRESULT,
}
MEMORYSTATUSEX :: struct {
dwLength: DWORD,
dwMemoryLoad: DWORD,
ullTotalPhys: DWORDLONG,
ullAvailPhys: DWORDLONG,
ullTotalPageFil: DWORDLONG,
ullAvailPageFil: DWORDLONG,
ullTotalVirtual: DWORDLONG,
ullAvailVirtual: DWORDLONG,
ullAvailExtendedVirtual: DWORDLONG,
}
Windows_Product_Type :: enum DWORD {
BUSINESS = 0x00000006, // Business
BUSINESS_N = 0x00000010, // Business N
CLUSTER_SERVER = 0x00000012, // HPC Edition
CLUSTER_SERVER_V = 0x00000040, // Server Hyper Core V
CORE = 0x00000065, // Windows 10 Home
CORE_COUNTRYSPECIFIC = 0x00000063, // Windows 10 Home China
CORE_N = 0x00000062, // Windows 10 Home N
CORE_SINGLELANGUAGE = 0x00000064, // Windows 10 Home Single Language
DATACENTER_EVALUATION_SERVER = 0x00000050, // Server Datacenter (evaluation installation)
DATACENTER_A_SERVER_CORE = 0x00000091, // Server Datacenter, Semi-Annual Channel (core installation)
STANDARD_A_SERVER_CORE = 0x00000092, // Server Standard, Semi-Annual Channel (core installation)
DATACENTER_SERVER = 0x00000008, // Server Datacenter (full installation. For Server Core installations of Windows Server 2012 and later, use the method, Determining whether Server Core is running.)
DATACENTER_SERVER_CORE = 0x0000000C, // Server Datacenter (core installation, Windows Server 2008 R2 and earlier)
DATACENTER_SERVER_CORE_V = 0x00000027, // Server Datacenter without Hyper-V (core installation)
DATACENTER_SERVER_V = 0x00000025, // Server Datacenter without Hyper-V (full installation)
EDUCATION = 0x00000079, // Windows 10 Education
EDUCATION_N = 0x0000007A, // Windows 10 Education N
ENTERPRISE = 0x00000004, // Windows 10 Enterprise
ENTERPRISE_E = 0x00000046, // Windows 10 Enterprise E
ENTERPRISE_EVALUATION = 0x00000048, // Windows 10 Enterprise Evaluation
ENTERPRISE_N = 0x0000001B, // Windows 10 Enterprise N
ENTERPRISE_N_EVALUATION = 0x00000054, // Windows 10 Enterprise N Evaluation
ENTERPRISE_S = 0x0000007D, // Windows 10 Enterprise 2015 LTSB
ENTERPRISE_S_EVALUATION = 0x00000081, // Windows 10 Enterprise 2015 LTSB Evaluation
ENTERPRISE_S_N = 0x0000007E, // Windows 10 Enterprise 2015 LTSB N
ENTERPRISE_S_N_EVALUATION = 0x00000082, // Windows 10 Enterprise 2015 LTSB N Evaluation
ENTERPRISE_SERVER = 0x0000000A, // Server Enterprise (full installation)
ENTERPRISE_SERVER_CORE = 0x0000000E, // Server Enterprise (core installation)
ENTERPRISE_SERVER_CORE_V = 0x00000029, // Server Enterprise without Hyper-V (core installation)
ENTERPRISE_SERVER_IA64 = 0x0000000F, // Server Enterprise for Itanium-based Systems
ENTERPRISE_SERVER_V = 0x00000026, // Server Enterprise without Hyper-V (full installation)
ESSENTIALBUSINESS_SERVER_ADDL = 0x0000003C, // Windows Essential Server Solution Additional
ESSENTIALBUSINESS_SERVER_ADDLSVC = 0x0000003E, // Windows Essential Server Solution Additional SVC
ESSENTIALBUSINESS_SERVER_MGMT = 0x0000003B, // Windows Essential Server Solution Management
ESSENTIALBUSINESS_SERVER_MGMTSVC = 0x0000003D, // Windows Essential Server Solution Management SVC
HOME_BASIC = 0x00000002, // Home Basic
HOME_BASIC_E = 0x00000043, // Not supported
HOME_BASIC_N = 0x00000005, // Home Basic N
HOME_PREMIUM = 0x00000003, // Home Premium
HOME_PREMIUM_E = 0x00000044, // Not supported
HOME_PREMIUM_N = 0x0000001A, // Home Premium N
HOME_PREMIUM_SERVER = 0x00000022, // Windows Home Server 2011
HOME_SERVER = 0x00000013, // Windows Storage Server 2008 R2 Essentials
HYPERV = 0x0000002A, // Microsoft Hyper-V Server
IOTENTERPRISE = 0x000000BC, // Windows IoT Enterprise
IOTENTERPRISE_S = 0x000000BF, // Windows IoT Enterprise LTSC
IOTUAP = 0x0000007B, // Windows 10 IoT Core
IOTUAPCOMMERCIAL = 0x00000083, // Windows 10 IoT Core Commercial
MEDIUMBUSINESS_SERVER_MANAGEMENT = 0x0000001E, // Windows Essential Business Server Management Server
MEDIUMBUSINESS_SERVER_MESSAGING = 0x00000020, // Windows Essential Business Server Messaging Server
MEDIUMBUSINESS_SERVER_SECURITY = 0x0000001F, // Windows Essential Business Server Security Server
MOBILE_CORE = 0x00000068, // Windows 10 Mobile
MOBILE_ENTERPRISE = 0x00000085, // Windows 10 Mobile Enterprise
MULTIPOINT_PREMIUM_SERVER = 0x0000004D, // Windows MultiPoint Server Premium (full installation)
MULTIPOINT_STANDARD_SERVER = 0x0000004C, // Windows MultiPoint Server Standard (full installation)
PRO_WORKSTATION = 0x000000A1, // Windows 10 Pro for Workstations
PRO_WORKSTATION_N = 0x000000A2, // Windows 10 Pro for Workstations N
PROFESSIONAL = 0x00000030, // Windows 10 Pro
PROFESSIONAL_E = 0x00000045, // Not supported
PROFESSIONAL_N = 0x00000031, // Windows 10 Pro N
PROFESSIONAL_WMC = 0x00000067, // Professional with Media Center
SB_SOLUTION_SERVER = 0x00000032, // Windows Small Business Server 2011 Essentials
SB_SOLUTION_SERVER_EM = 0x00000036, // Server For SB Solutions EM
SERVER_FOR_SB_SOLUTIONS = 0x00000033, // Server For SB Solutions
SERVER_FOR_SB_SOLUTIONS_EM = 0x00000037, // Server For SB Solutions EM
SERVER_FOR_SMALLBUSINESS = 0x00000018, // Windows Server 2008 for Windows Essential Server Solutions
SERVER_FOR_SMALLBUSINESS_V = 0x00000023, // Windows Server 2008 without Hyper-V for Windows Essential Server Solutions
SERVER_FOUNDATION = 0x00000021, // Server Foundation
SMALLBUSINESS_SERVER = 0x00000009, // Windows Small Business Server
SMALLBUSINESS_SERVER_PREMIUM = 0x00000019, // Small Business Server Premium
SMALLBUSINESS_SERVER_PREMIUM_CORE = 0x0000003F, // Small Business Server Premium (core installation)
SOLUTION_EMBEDDEDSERVER = 0x00000038, // Windows MultiPoint Server
STANDARD_EVALUATION_SERVER = 0x0000004F, // Server Standard (evaluation installation)
STANDARD_SERVER = 0x00000007, // Server Standard (full installation. For Server Core installations of Windows Server 2012 and later, use the method, Determining whether Server Core is running.)
STANDARD_SERVER_CORE = 0x0000000D, // Server Standard (core installation, Windows Server 2008 R2 and earlier)
STANDARD_SERVER_CORE_V = 0x00000028, // Server Standard without Hyper-V (core installation)
STANDARD_SERVER_V = 0x00000024, // Server Standard without Hyper-V
STANDARD_SERVER_SOLUTIONS = 0x00000034, // Server Solutions Premium
STANDARD_SERVER_SOLUTIONS_CORE = 0x00000035, // Server Solutions Premium (core installation)
STARTER = 0x0000000B, // Starter
STARTER_E = 0x00000042, // Not supported
STARTER_N = 0x0000002F, // Starter N
STORAGE_ENTERPRISE_SERVER = 0x00000017, // Storage Server Enterprise
STORAGE_ENTERPRISE_SERVER_CORE = 0x0000002E, // Storage Server Enterprise (core installation)
STORAGE_EXPRESS_SERVER = 0x00000014, // Storage Server Express
STORAGE_EXPRESS_SERVER_CORE = 0x0000002B, // Storage Server Express (core installation)
STORAGE_STANDARD_EVALUATION_SERVER = 0x00000060, // Storage Server Standard (evaluation installation)
STORAGE_STANDARD_SERVER = 0x00000015, // Storage Server Standard
STORAGE_STANDARD_SERVER_CORE = 0x0000002C, // Storage Server Standard (core installation)
STORAGE_WORKGROUP_EVALUATION_SERVER = 0x0000005F, // Storage Server Workgroup (evaluation installation)
STORAGE_WORKGROUP_SERVER = 0x00000016, // Storage Server Workgroup
STORAGE_WORKGROUP_SERVER_CORE = 0x0000002D, // Storage Server Workgroup (core installation)
ULTIMATE = 0x00000001, // Ultimate
ULTIMATE_E = 0x00000047, // Not supported
ULTIMATE_N = 0x0000001C, // Ultimate N
UNDEFINED = 0x00000000, // An unknown product
WEB_SERVER = 0x00000011, // Web Server (full installation)
WEB_SERVER_CORE = 0x0000001D, // Web Server (core installation)
}