diff --git a/windows/syscall_windows.go b/windows/syscall_windows.go index 92ce02bb..be8c50f2 100644 --- a/windows/syscall_windows.go +++ b/windows/syscall_windows.go @@ -295,6 +295,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys stringFromGUID2(rguid *GUID, lpsz *uint16, cchMax int32) (chars int32) = ole32.StringFromGUID2 //sys coCreateGuid(pguid *GUID) (ret error) = ole32.CoCreateGuid //sys coTaskMemFree(address unsafe.Pointer) = ole32.CoTaskMemFree +//sys rtlGetVersion(info *OsVersionInfoEx) (ret error) = ntdll.RtlGetVersion // syscall interface implementation for other packages @@ -1304,3 +1305,16 @@ func (t Token) KnownFolderPath(folderID *KNOWNFOLDERID, flags uint32) (string, e defer coTaskMemFree(unsafe.Pointer(p)) return UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:]), nil } + +// RtlGetVersion returns the true version of the underlying operating system, ignoring +// any manifesting or compatibility layers on top of the win32 layer. +func RtlGetVersion() *OsVersionInfoEx { + info := &OsVersionInfoEx{} + info.osVersionInfoSize = uint32(unsafe.Sizeof(*info)) + // According to documentation, this function always succeeds. + // The function doesn't even check the validity of the + // osVersionInfoSize member. Disassembling ntdll.dll indicates + // that the documentation is indeed correct about that. + _ = rtlGetVersion(info) + return info +} diff --git a/windows/syscall_windows_test.go b/windows/syscall_windows_test.go index f1d5ec18..ff9689a2 100644 --- a/windows/syscall_windows_test.go +++ b/windows/syscall_windows_test.go @@ -216,3 +216,10 @@ func TestKnownFolderPath(t *testing.T) { t.Fatalf("Path = %q; want %q", got, want) } } + +func TestRtlGetVersion(t *testing.T) { + version := windows.RtlGetVersion() + if version.MajorVersion < 6 { + t.Fatalf("MajorVersion = %d; want >= 6", version.MajorVersion) + } +} diff --git a/windows/types_windows.go b/windows/types_windows.go index 1cba11ed..8a563f92 100644 --- a/windows/types_windows.go +++ b/windows/types_windows.go @@ -1649,3 +1649,17 @@ const ( KF_FLAG_SIMPLE_IDLIST = 0x00000100 KF_FLAG_ALIAS_ONLY = 0x80000000 ) + +type OsVersionInfoEx struct { + osVersionInfoSize uint32 + MajorVersion uint32 + MinorVersion uint32 + BuildNumber uint32 + PlatformId uint32 + CsdVersion [128]uint16 + ServicePackMajor uint16 + ServicePackMinor uint16 + SuiteMask uint16 + ProductType byte + _ byte +} diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go index e66ab049..9e43e966 100644 --- a/windows/zsyscall_windows.go +++ b/windows/zsyscall_windows.go @@ -43,6 +43,7 @@ var ( modcrypt32 = NewLazySystemDLL("crypt32.dll") moduser32 = NewLazySystemDLL("user32.dll") modole32 = NewLazySystemDLL("ole32.dll") + modntdll = NewLazySystemDLL("ntdll.dll") modws2_32 = NewLazySystemDLL("ws2_32.dll") moddnsapi = NewLazySystemDLL("dnsapi.dll") modiphlpapi = NewLazySystemDLL("iphlpapi.dll") @@ -232,6 +233,7 @@ var ( procStringFromGUID2 = modole32.NewProc("StringFromGUID2") procCoCreateGuid = modole32.NewProc("CoCreateGuid") procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") + procRtlGetVersion = modntdll.NewProc("RtlGetVersion") procWSAStartup = modws2_32.NewProc("WSAStartup") procWSACleanup = modws2_32.NewProc("WSACleanup") procWSAIoctl = modws2_32.NewProc("WSAIoctl") @@ -2520,6 +2522,14 @@ func coTaskMemFree(address unsafe.Pointer) { return } +func rtlGetVersion(info *OsVersionInfoEx) (ret error) { + r0, _, _ := syscall.Syscall(procRtlGetVersion.Addr(), 1, uintptr(unsafe.Pointer(info)), 0, 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} + func WSAStartup(verreq uint32, data *WSAData) (sockerr error) { r0, _, _ := syscall.Syscall(procWSAStartup.Addr(), 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0) if r0 != 0 {