Skip to content

Commit

Permalink
windows: allow retrieving true version with RtlGetNtVersionNumbers
Browse files Browse the repository at this point in the history
While RtlGetVersion was added so that users can get the Windows version
that isn't affected by manifesting, RtlGetVersion is still stubbed out
by the application compatibility layer (aclayers.dll and apphelp.dll)
for certain processes, such as msiexec.exe, rendering these functions
useless for actually determining the underlying operating system. This
matters in the case of msiexec.exe using a custom action DLL to install
a kernel driver, which of course is version specific. This is also
useful, it turns out, for the C runtime library, in which Microsoft uses
this function too. It's existed as a stable interface since Windows XP,
has Wine support, and is used in a decent amount of software.

Change-Id: If391e43bc6d798eff6803d5a7aa6a179f2b31d88
Reviewed-on: https://go-review.googlesource.com/c/sys/+/188119
Run-TryBot: Jason A. Donenfeld <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Alex Brainman <[email protected]>
  • Loading branch information
zx2c4 committed Aug 25, 2019
1 parent fde4db3 commit fb81701
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 4 deletions.
13 changes: 11 additions & 2 deletions windows/syscall_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
//sys coCreateGuid(pguid *GUID) (ret error) = ole32.CoCreateGuid
//sys CoTaskMemFree(address unsafe.Pointer) = ole32.CoTaskMemFree
//sys rtlGetVersion(info *OsVersionInfoEx) (ret error) = ntdll.RtlGetVersion
//sys rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) = ntdll.RtlGetNtVersionNumbers

// syscall interface implementation for other packages

Expand Down Expand Up @@ -1306,8 +1307,8 @@ func (t Token) KnownFolderPath(folderID *KNOWNFOLDERID, flags uint32) (string, e
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.
// RtlGetVersion returns the version of the underlying operating system, ignoring
// manifest semantics but is affected by the application compatibility layer.
func RtlGetVersion() *OsVersionInfoEx {
info := &OsVersionInfoEx{}
info.osVersionInfoSize = uint32(unsafe.Sizeof(*info))
Expand All @@ -1318,3 +1319,11 @@ func RtlGetVersion() *OsVersionInfoEx {
_ = rtlGetVersion(info)
return info
}

// RtlGetNtVersionNumbers returns the version of the underlying operating system,
// ignoring manifest semantics and the application compatibility layer.
func RtlGetNtVersionNumbers() (majorVersion, minorVersion, buildNumber uint32) {
rtlGetNtVersionNumbers(&majorVersion, &minorVersion, &buildNumber)
buildNumber &= 0xffff
return
}
7 changes: 5 additions & 2 deletions windows/syscall_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,10 @@ func TestKnownFolderPath(t *testing.T) {

func TestRtlGetVersion(t *testing.T) {
version := windows.RtlGetVersion()
if version.MajorVersion < 6 {
t.Fatalf("MajorVersion = %d; want >= 6", version.MajorVersion)
major, minor, build := windows.RtlGetNtVersionNumbers()
// Go is not explictly added to the application compatibility database, so
// these two functions should return the same thing.
if version.MajorVersion != major || version.MinorVersion != minor || version.BuildNumber != build {
t.Fatalf("%d.%d.%d != %d.%d.%d", version.MajorVersion, version.MinorVersion, version.BuildNumber, major, minor, build)
}
}
6 changes: 6 additions & 0 deletions windows/zsyscall_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit fb81701

Please sign in to comment.