The simplest version APIs all lie unless you have a manifest. If you want to be sure you get the real version no matter what, there's various more complicated techniques. The one I had been using involved reading the version info from kernel32.dll. This way is easier, and involves a neat technique. The KUSER_SHARED_DATA type is always resident in memory. You can declare it, then copy it, with no APIs besides CopyMemory.
It also returns a ton of other info.
There's a lot more on the end, but it's not particularly useful, varies from version to version. The one given will work on XP-11.
Then using it is as simple as:
The address is the same for both 32bit and 64bit, so need for an alternate version.
It also returns a ton of other info.
Code:
Public Type LARGE_INTEGER
#If (TWINBASIC = 1) Or (Win64 = 1) Then
QuadPart As LongLong
#Else
lowpart As Long
highpart As Long
#End If
End Type
Public Type KSYSTEM_TIME
LowPart As Long '0x0
High1Time As Long '0x4
High2Time As Long '0x8
End Type
Public Enum NT_PRODUCT_TYPE
NtProductWinNt = 1
NtProductLanManNt = 2
NtProductServer = 3
End Enum
Public Enum ALTERNATIVE_ARCHITECTURE_TYPE
StandardDesign = 0
NEC98x86 = 1
EndAlternatives = 2
End Enum
Public Enum VER_SUITE_VALUES
VER_SERVER_NT = &H80000000
VER_WORKSTATION_NT = &H40000000
VER_SUITE_SMALLBUSINESS = &H00000001
VER_SUITE_ENTERPRISE = &H00000002
VER_SUITE_BACKOFFICE = &H00000004
VER_SUITE_COMMUNICATIONS = &H00000008
VER_SUITE_TERMINAL = &H00000010
VER_SUITE_SMALLBUSINESS_RESTRICTED = &H00000020
VER_SUITE_EMBEDDEDNT = &H00000040
VER_SUITE_DATACENTER = &H00000080
VER_SUITE_SINGLEUSERTS = &H00000100
VER_SUITE_PERSONAL = &H00000200
VER_SUITE_BLADE = &H00000400
VER_SUITE_EMBEDDED_RESTRICTED = &H00000800
VER_SUITE_SECURITY_APPLIANCE = &H00001000
VER_SUITE_STORAGE_SERVER = &H00002000
VER_SUITE_COMPUTE_SERVER = &H00004000
VER_SUITE_WH_SERVER = &H00008000&
VER_SUITE_MULTIUSERTS = &H00020000
End Enum
Public Type KUSER_SHARED_DATA
TickCountLowDeprecated As Long '0x0
TickCountMultiplier As Long '0x4
InterruptTime As KSYSTEM_TIME '0x8
SystemTime As KSYSTEM_TIME '0x14
TimeZoneBias As KSYSTEM_TIME '0x20
ImageNumberLow As Integer '0x2c
ImageNumberHigh As Integer '0x2e
NtSystemRoot(0 To 259) As Integer '0x30
MaxStackTraceDepth As Long '0x238
CryptoExponent As Long '0x23c
TimeZoneId As Long '0x240
LargePageMinimum As Long '0x244
' Reserved2(0 To 6) As Long '0x248
AitSamplingValue As Long '0x24C
AppCompatFlag As Long '0x250
#If (TWINBASIC = 1) Or (Win64 = 1) Then
RNGSeedVersion As LongLong
#Else
RNGSeedVersion As Currency
#End If
GlobalValidationRunlevel As Long
TimeZoneBiasStamp As Long
NtBuildNumber As Long
NtProductType As NT_PRODUCT_TYPE '0x264
ProductTypeIsValid As Byte '0x268
Reserved0 As Byte
NativeProcessorArchitecture As Integer
NtMajorVersion As Long '0x26c
NtMinorVersion As Long '0x270
ProcessorFeatures(0 To 63) As Byte '0x274
Reserved1 As Long '0x2b4
Reserved3 As Long '0x2b8
TimeSlip As Long '0x2bc
AlternativeArchitecture As ALTERNATIVE_ARCHITECTURE_TYPE '0x2c0
BootId As Long 'Windows 10+ only
SystemExpirationDate As LARGE_INTEGER '0x2c8
SuiteMask As VER_SUITE_VALUES '0x2d0
KdDebuggerEnabled As Byte '0x2d4
MitigationPolicies As Byte '0x2d5
CyclesPerYield As Integer 'Only on Win10 1903 and higher
ActiveConsoleId As Long '0x2d8
DismountCount As Long '0x2dc
ComPlusPackage As Long '0x2e0
LastSystemRITEventTickCount As Long '0x2e4
NumberOfPhysicalPages As Long '0x2e8
SafeBootMode As Byte '0x2ec
VirtualizationFlags As Byte
Reserved12(1) As Byte
SharedDataFlags As Long '0x2f0 NOTE: TraceLogging on 2k/XP
DataFlagsPad(0) As Long
#If (TWINBASIC = 1) Or (Win64 = 1) Then
TestRetInstruction As LongLong '0x2f8
#Else
TestRetInstruction As Currency
#End If
SystemCall As Long '0x300
SystemCallReturn As Long '0x304
#If (TWINBASIC = 1) Or (Win64 = 1) Then
SystemCallPad(0 To 2) As LongLong '0x308
TickCountQuad As LongLong '0x320
#Else
SystemCallPad(0 To 2) As Currency
TickCountQuad As Currency
#End If
'union
'{
' volatile struct _KSYSTEM_TIME TickCount; //0x320
'TickCount As KSYSTEM_TIME
ReservedTickCountOverlay(1) As Long 'Since not using _KSYSTEM_TIME
'};
Cookie As Long '0x330
'Wow64SharedInformation(0 To 15) As Long '0x334
End Type
Then using it is as simple as:
Code:
#If VBA7 Then
Public Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
#Else
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
#End If
Private Sub ReadRealVersion()
Dim kusd As KUSER_SHARED_DATA
CopyMemory kusd, ByVal &H7ffe0000, LenB(kusd)
Debug.Print kusd.NtMajorVersion & "." & kusd.NtMinorVersion & "." & kusd.NtBuildNumber
End Sub