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.
The version info can be extracted directly. To use this, you need no declares besides CopyMemory:
That's based on the offsets of the members of the full type.Code:Dim dwMajor As Long, dwMinor As Long, dwBuild As Long CopyMemory dwMajor, ByVal &H7FFE026C, 4 CopyMemory dwMinor, ByVal &H7FFE0270, 4 CopyMemory dwBuild, ByVal &H7FFE0260, 4 Debug.Print dwMajor & "." & dwMinor & "." & dwBuild
If you're interested in the full type, the following is the expanded form: 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.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:
The address is the same for both 32bit and 64bit, so need for an alternate version.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
The type as provided here is compatible with XP through 11. Build number is only correct on 10/11, but it's much less significant earlier anyway. The type can be truncated after ProcessorFeatures for Windows 2000 and NT4 compatibility-- the top snippet without declares works on NT4-11.




Reply With Quote