200 lines
5.9 KiB
Go
200 lines
5.9 KiB
Go
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
//go:build windows
|
|
|
|
package wingoes
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"golang.org/x/sys/windows"
|
|
"golang.org/x/sys/windows/registry"
|
|
)
|
|
|
|
var (
|
|
verOnce sync.Once
|
|
verInfo osVersionInfo // must access via getVersionInfo()
|
|
)
|
|
|
|
// osVersionInfo is more compact than windows.OsVersionInfoEx, which contains
|
|
// extraneous information.
|
|
type osVersionInfo struct {
|
|
major uint32
|
|
minor uint32
|
|
build uint32
|
|
servicePack uint16
|
|
str string
|
|
isDC bool
|
|
isServer bool
|
|
}
|
|
|
|
const (
|
|
_VER_NT_WORKSTATION = 1
|
|
_VER_NT_DOMAIN_CONTROLLER = 2
|
|
_VER_NT_SERVER = 3
|
|
)
|
|
|
|
func getVersionInfo() *osVersionInfo {
|
|
verOnce.Do(func() {
|
|
osv := windows.RtlGetVersion()
|
|
verInfo = osVersionInfo{
|
|
major: osv.MajorVersion,
|
|
minor: osv.MinorVersion,
|
|
build: osv.BuildNumber,
|
|
servicePack: osv.ServicePackMajor,
|
|
str: fmt.Sprintf("%d.%d.%d", osv.MajorVersion, osv.MinorVersion, osv.BuildNumber),
|
|
isDC: osv.ProductType == _VER_NT_DOMAIN_CONTROLLER,
|
|
// Domain Controllers are also implicitly servers.
|
|
isServer: osv.ProductType == _VER_NT_DOMAIN_CONTROLLER || osv.ProductType == _VER_NT_SERVER,
|
|
}
|
|
// UBR is only available on Windows 10 and 11 (MajorVersion == 10).
|
|
if osv.MajorVersion == 10 {
|
|
if ubr, err := getUBR(); err == nil {
|
|
verInfo.str = fmt.Sprintf("%s.%d", verInfo.str, ubr)
|
|
}
|
|
}
|
|
})
|
|
return &verInfo
|
|
}
|
|
|
|
// getUBR returns the "update build revision," ie. the fourth component of the
|
|
// version string found on Windows 10 and Windows 11 systems.
|
|
func getUBR() (uint32, error) {
|
|
key, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
|
`SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE|registry.WOW64_64KEY)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer key.Close()
|
|
|
|
val, valType, err := key.GetIntegerValue("UBR")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if valType != registry.DWORD {
|
|
return 0, registry.ErrUnexpectedType
|
|
}
|
|
|
|
return uint32(val), nil
|
|
}
|
|
|
|
// GetOSVersionString returns the Windows version of the current machine in
|
|
// dotted-decimal form. The version string contains 3 components on Windows 7
|
|
// and 8.x, and 4 components on Windows 10 and 11.
|
|
func GetOSVersionString() string {
|
|
return getVersionInfo().String()
|
|
}
|
|
|
|
// IsWinServer returns true if and only if this computer's version of Windows is
|
|
// a server edition.
|
|
func IsWinServer() bool {
|
|
return getVersionInfo().isServer
|
|
}
|
|
|
|
// IsWinDomainController returs true if this computer's version of Windows is
|
|
// configured to act as a domain controller.
|
|
func IsWinDomainController() bool {
|
|
return getVersionInfo().isDC
|
|
}
|
|
|
|
// IsWin7SP1OrGreater returns true when running on Windows 7 SP1 or newer.
|
|
func IsWin7SP1OrGreater() bool {
|
|
if IsWin8OrGreater() {
|
|
return true
|
|
}
|
|
|
|
vi := getVersionInfo()
|
|
return vi.major == 6 && vi.minor == 1 && vi.servicePack > 0
|
|
}
|
|
|
|
// IsWin8OrGreater returns true when running on Windows 8.0 or newer.
|
|
func IsWin8OrGreater() bool {
|
|
return getVersionInfo().isVersionOrGreater(6, 2, 0)
|
|
}
|
|
|
|
// IsWin8Point1OrGreater returns true when running on Windows 8.1 or newer.
|
|
func IsWin8Point1OrGreater() bool {
|
|
return getVersionInfo().isVersionOrGreater(6, 3, 0)
|
|
}
|
|
|
|
// IsWin10OrGreater returns true when running on any build of Windows 10 or newer.
|
|
func IsWin10OrGreater() bool {
|
|
return getVersionInfo().major >= 10
|
|
}
|
|
|
|
// Win10BuildConstant encodes build numbers for the various editions of Windows 10,
|
|
// for use with IsWin10BuildOrGreater.
|
|
type Win10BuildConstant uint32
|
|
|
|
const (
|
|
Win10BuildRTM = Win10BuildConstant(10240)
|
|
Win10Build1511 = Win10BuildConstant(10586)
|
|
Win10Build1607 = Win10BuildConstant(14393)
|
|
Win10BuildAnniversary = Win10Build1607
|
|
Win10Build1703 = Win10BuildConstant(15063)
|
|
Win10BuildCreators = Win10Build1703
|
|
Win10Build1709 = Win10BuildConstant(16299)
|
|
Win10BuildFallCreators = Win10Build1709
|
|
Win10Build1803 = Win10BuildConstant(17134)
|
|
Win10Build1809 = Win10BuildConstant(17763)
|
|
Win10Build1903 = Win10BuildConstant(18362)
|
|
Win10Build1909 = Win10BuildConstant(18363)
|
|
Win10Build2004 = Win10BuildConstant(19041)
|
|
Win10Build20H2 = Win10BuildConstant(19042)
|
|
Win10Build21H1 = Win10BuildConstant(19043)
|
|
Win10Build21H2 = Win10BuildConstant(19044)
|
|
Win10Build22H2 = Win10BuildConstant(19045)
|
|
)
|
|
|
|
// IsWin10BuildOrGreater returns true when running on the specified Windows 10
|
|
// build, or newer.
|
|
func IsWin10BuildOrGreater(build Win10BuildConstant) bool {
|
|
return getVersionInfo().isWin10BuildOrGreater(uint32(build))
|
|
}
|
|
|
|
// Win11BuildConstant encodes build numbers for the various editions of Windows 11,
|
|
// for use with IsWin11BuildOrGreater.
|
|
type Win11BuildConstant uint32
|
|
|
|
const (
|
|
Win11BuildRTM = Win11BuildConstant(22000)
|
|
Win11Build22H2 = Win11BuildConstant(22621)
|
|
Win11Build23H2 = Win11BuildConstant(22631)
|
|
)
|
|
|
|
// IsWin11OrGreater returns true when running on any release of Windows 11,
|
|
// or newer.
|
|
func IsWin11OrGreater() bool {
|
|
return IsWin11BuildOrGreater(Win11BuildRTM)
|
|
}
|
|
|
|
// IsWin11BuildOrGreater returns true when running on the specified Windows 11
|
|
// build, or newer.
|
|
func IsWin11BuildOrGreater(build Win11BuildConstant) bool {
|
|
// Under the hood, Windows 11 is just Windows 10 with a sufficiently advanced
|
|
// build number.
|
|
return getVersionInfo().isWin10BuildOrGreater(uint32(build))
|
|
}
|
|
|
|
func (osv *osVersionInfo) String() string {
|
|
return osv.str
|
|
}
|
|
|
|
func (osv *osVersionInfo) isWin10BuildOrGreater(build uint32) bool {
|
|
return osv.isVersionOrGreater(10, 0, build)
|
|
}
|
|
|
|
func (osv *osVersionInfo) isVersionOrGreater(major, minor, build uint32) bool {
|
|
return isVerGE(osv.major, major, osv.minor, minor, osv.build, build)
|
|
}
|
|
|
|
func isVerGE(lmajor, rmajor, lminor, rminor, lbuild, rbuild uint32) bool {
|
|
return lmajor > rmajor ||
|
|
lmajor == rmajor &&
|
|
(lminor > rminor ||
|
|
lminor == rminor && lbuild >= rbuild)
|
|
}
|