Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+195
-66
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user