-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathosversion.go
203 lines (173 loc) · 6.06 KB
/
osversion.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// 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)
WinServer2016 = Win10Build1607
Win10BuildAnniversary = Win10Build1607
Win10Build1703 = Win10BuildConstant(15063)
Win10BuildCreators = Win10Build1703
Win10Build1709 = Win10BuildConstant(16299)
Win10BuildFallCreators = Win10Build1709
Win10Build1803 = Win10BuildConstant(17134)
Win10Build1809 = Win10BuildConstant(17763)
WinServer2019 = Win10Build1809
Win10Build1903 = Win10BuildConstant(18362)
Win10Build1909 = Win10BuildConstant(18363)
Win10Build2004 = Win10BuildConstant(19041)
Win10Build20H2 = Win10BuildConstant(19042)
Win10Build21H1 = Win10BuildConstant(19043)
Win10Build21H2 = Win10BuildConstant(19044)
Win10Build22H2 = Win10BuildConstant(19045)
WinServer2022 = Win10BuildConstant(20348)
)
// 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)
Win11Build24H2 = Win11BuildConstant(26100)
)
// 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)
}