This commit is contained in:
bluepython508 committed 2026-10-22 12:27:19 +01:00
1 parent 3530afb854
commit 6903fcff27
2146 files changed
+112256 -190703

No files matched your search

+195 -66
View File
@@ -9,27 +9,33 @@ package wingoes
import (
"fmt"
"sync"
"sync/atomic"
"unsafe"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
var (
verOnce sync.Once
verInfo osVersionInfo // must access via getVersionInfo()
const (
_VER_MINORVERSION = 0x0000001
_VER_MAJORVERSION = 0x0000002
_VER_BUILDNUMBER = 0x0000004
_VER_PLATFORMID = 0x0000008
_VER_SERVICEPACKMINOR = 0x0000010
_VER_SERVICEPACKMAJOR = 0x0000020
_VER_SUITENAME = 0x0000040
_VER_PRODUCT_TYPE = 0x0000080
)
// 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_EQUAL = 1
_VER_GREATER = 2
_VER_GREATER_EQUAL = 3
_VER_LESS = 4
_VER_LESS_EQUAL = 5
_VER_AND = 6
_VER_OR = 7
)
const (
_VER_NT_WORKSTATION = 1
@@ -37,29 +43,124 @@ const (
_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,
// The definition in x/sys/windows doesn't export the OSVersionInfoSize field,
// which we need to set before making API calls.
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 // Reserved
}
type osVersionInfo struct {
fallbackBuildMin atomic.Uint32
fallbackBuildMax atomic.Uint32
build uint32
str string
isDC bool
isServer bool
useFallback bool
}
var (
// We cannot use mksyscall for these because they pass uint64 by value on
// 32-bit CPU architectures.
procVerSetConditionMask = modkernel32.NewProc("VerSetConditionMask")
procVerifyVersionInfo = modkernel32.NewProc("VerifyVersionInfoW")
verOnce sync.Once
verInfo osVersionInfo // must access via getVersionInfo()
)
func getVersionInfoInternal() (*_OSVERSIONINFOEX, error) {
osv := _OSVERSIONINFOEX{
OSVersionInfoSize: uint32(unsafe.Sizeof(_OSVERSIONINFOEX{})),
}
// Using GetVersionEx to account for app manifest.
if err := getVersionEx(&osv); err != nil {
return nil, err
}
return &osv, nil
}
func (osv *osVersionInfo) initFlags() {
if isDC, err := verQueryProductType(_VER_NT_DOMAIN_CONTROLLER); err == nil {
osv.isDC = isDC
if isDC {
// Domain Controllers are also implicitly servers.
isServer: osv.ProductType == _VER_NT_DOMAIN_CONTROLLER || osv.ProductType == _VER_NT_SERVER,
osv.isServer = true
return
}
// 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)
}
}
if isServer, err := verQueryProductType(_VER_NT_SERVER); err == nil {
osv.isServer = isServer
}
}
func (osv *osVersionInfo) initFallback() {
osv.initFlags()
osv.useFallback = true
}
func verCheckManifest(osv *_OSVERSIONINFOEX) bool {
major, minor, build := windows.RtlGetNtVersionNumbers()
return major == osv.MajorVersion && minor == osv.MinorVersion && build == osv.BuildNumber
}
func (osv *osVersionInfo) init() {
osvx, err := getVersionInfoInternal()
if err != nil {
osv.initFallback()
return
}
// We only support 10.0.x.x
if osvx.MajorVersion != 10 || osvx.MinorVersion != 0 {
if verCheckManifest(osvx) {
panic("change in versioning scheme -- package must be updated to interpret")
} else {
panic("incoherent Windows version -- missing/outdated manifest?")
}
})
}
*osv = osVersionInfo{
build: osvx.BuildNumber,
str: versionString(osvx),
isDC: osvx.ProductType == _VER_NT_DOMAIN_CONTROLLER,
// Domain Controllers are also implicitly servers.
isServer: osvx.ProductType == _VER_NT_DOMAIN_CONTROLLER || osvx.ProductType == _VER_NT_SERVER,
}
}
func getVersionInfo() *osVersionInfo {
verOnce.Do(verInfo.init)
return &verInfo
}
func versionString(osv *_OSVERSIONINFOEX) string {
fmtstr := "%d.%d.%d.%d"
vers := append(make([]any, 0, 4),
uint(osv.MajorVersion),
uint(osv.MinorVersion),
uint(osv.BuildNumber),
)
if ubr, err := getUBR(); err == nil {
vers = append(vers, uint(ubr))
} else {
fmtstr = fmtstr[:len(fmtstr)-3]
}
return fmt.Sprintf(fmtstr, vers...)
}
// 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) {
@@ -82,8 +183,7 @@ func getUBR() (uint32, error) {
}
// 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.
// dotted-decimal form.
func GetOSVersionString() string {
return getVersionInfo().String()
}
@@ -100,31 +200,6 @@ 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
@@ -133,6 +208,7 @@ const (
Win10BuildRTM = Win10BuildConstant(10240)
Win10Build1511 = Win10BuildConstant(10586)
Win10Build1607 = Win10BuildConstant(14393)
WinServer2016 = Win10Build1607
Win10BuildAnniversary = Win10Build1607
Win10Build1703 = Win10BuildConstant(15063)
Win10BuildCreators = Win10Build1703
@@ -140,6 +216,7 @@ const (
Win10BuildFallCreators = Win10Build1709
Win10Build1803 = Win10BuildConstant(17134)
Win10Build1809 = Win10BuildConstant(17763)
WinServer2019 = Win10Build1809
Win10Build1903 = Win10BuildConstant(18362)
Win10Build1909 = Win10BuildConstant(18363)
Win10Build2004 = Win10BuildConstant(19041)
@@ -147,6 +224,7 @@ const (
Win10Build21H1 = Win10BuildConstant(19043)
Win10Build21H2 = Win10BuildConstant(19044)
Win10Build22H2 = Win10BuildConstant(19045)
WinServer2022 = Win10BuildConstant(20348)
)
// IsWin10BuildOrGreater returns true when running on the specified Windows 10
@@ -163,6 +241,10 @@ const (
Win11BuildRTM = Win11BuildConstant(22000)
Win11Build22H2 = Win11BuildConstant(22621)
Win11Build23H2 = Win11BuildConstant(22631)
Win11Build24H2 = Win11BuildConstant(26100)
WinServer2025 = Win11Build24H2
Win11Build25H2 = Win11BuildConstant(26200)
Win11Build26H1 = Win11BuildConstant(28000)
)
// IsWin11OrGreater returns true when running on any release of Windows 11,
@@ -184,16 +266,63 @@ func (osv *osVersionInfo) String() string {
}
func (osv *osVersionInfo) isWin10BuildOrGreater(build uint32) bool {
return osv.isVersionOrGreater(10, 0, build)
if !osv.useFallback {
return osv.build >= build
}
if osv.fallbackBuildMin.Load() >= build {
return true
}
if build >= osv.fallbackBuildMax.Load() {
return false
}
result, err := verQueryBuild(10, 0, build)
if err == nil {
if result {
osv.fallbackBuildMin.Store(build)
} else {
osv.fallbackBuildMax.Store(build)
}
}
return result
}
func (osv *osVersionInfo) isVersionOrGreater(major, minor, build uint32) bool {
return isVerGE(osv.major, major, osv.minor, minor, osv.build, build)
func verQueryBuild(major, minor, build uint32) (bool, error) {
var condMask uint64
condMask = verSetConditionMask(condMask, _VER_MAJORVERSION, _VER_GREATER_EQUAL)
condMask = verSetConditionMask(condMask, _VER_MINORVERSION, _VER_GREATER_EQUAL)
condMask = verSetConditionMask(condMask, _VER_BUILDNUMBER, _VER_GREATER_EQUAL)
typeMask := uint32(_VER_MAJORVERSION | _VER_MINORVERSION | _VER_BUILDNUMBER)
osv := _OSVERSIONINFOEX{
MajorVersion: major,
MinorVersion: minor,
BuildNumber: build,
}
return verVerify(&osv, typeMask, condMask)
}
func isVerGE(lmajor, rmajor, lminor, rminor, lbuild, rbuild uint32) bool {
return lmajor > rmajor ||
lmajor == rmajor &&
(lminor > rminor ||
lminor == rminor && lbuild >= rbuild)
func verQueryProductType(wantProdType byte) (bool, error) {
condMask := verSetConditionMask(0, _VER_PRODUCT_TYPE, _VER_EQUAL)
osv := _OSVERSIONINFOEX{
ProductType: wantProdType,
}
return verVerify(&osv, _VER_PRODUCT_TYPE, condMask)
}
func verVerify(osv *_OSVERSIONINFOEX, typeMask uint32, condMask uint64) (bool, error) {
osv.OSVersionInfoSize = uint32(unsafe.Sizeof(_OSVERSIONINFOEX{}))
err := verifyVersionInfo(osv, typeMask, condMask)
switch err {
case nil:
return true, nil
case windows.ERROR_OLD_WIN_VERSION:
return false, nil
default:
return false, err
}
}