Skip to content

Commit 58a4700

Browse files
authored
创建 WindowsVersion 工具类 (#3932)
1 parent 68c8a30 commit 58a4700

3 files changed

Lines changed: 205 additions & 43 deletions

File tree

HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/OperatingSystem.java

Lines changed: 22 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import org.jackhuang.hmcl.util.KeyValuePairUtils;
2121
import org.jackhuang.hmcl.util.platform.windows.Kernel32;
2222
import org.jackhuang.hmcl.util.platform.windows.WinTypes;
23+
import org.jackhuang.hmcl.util.platform.windows.WindowsVersion;
24+
import org.jetbrains.annotations.Nullable;
2325

2426
import java.io.BufferedReader;
2527
import java.io.IOException;
@@ -106,6 +108,11 @@ public String getJavaExecutable() {
106108
*/
107109
public static final int SYSTEM_BUILD_NUMBER;
108110

111+
/**
112+
* The version number is non-null if and only if the operating system is {@linkplain #WINDOWS}.
113+
*/
114+
public static final @Nullable WindowsVersion WINDOWS_VERSION;
115+
109116
/**
110117
* The name of current operating system.
111118
*/
@@ -125,8 +132,6 @@ public String getJavaExecutable() {
125132
private static final String[] INVALID_RESOURCE_BASENAMES;
126133
private static final String[] INVALID_RESOURCE_FULLNAMES;
127134

128-
private static final boolean IS_WINDOWS_7_OR_LATER;
129-
130135
static {
131136
String nativeEncoding = System.getProperty("native.encoding");
132137
String hmclNativeEncoding = System.getProperty("hmcl.native.encoding");
@@ -152,61 +157,35 @@ public String getJavaExecutable() {
152157
NATIVE_CHARSET = nativeCharset;
153158

154159
if (CURRENT_OS == WINDOWS) {
155-
String versionNumber = null;
156-
int buildNumber = -1;
157160
int codePage = -1;
161+
WindowsVersion windowsVersion = null;
158162

159163
Kernel32 kernel32 = Kernel32.INSTANCE;
160-
161164
// Get Windows version number
162165
if (kernel32 != null) {
163166
WinTypes.OSVERSIONINFOEXW osVersionInfo = new WinTypes.OSVERSIONINFOEXW();
164167
if (kernel32.GetVersionExW(osVersionInfo)) {
165-
int majorVersion = osVersionInfo.dwMajorVersion;
166-
int minorVersion = osVersionInfo.dwMinorVersion;
167-
168-
buildNumber = osVersionInfo.dwBuildNumber;
169-
versionNumber = majorVersion + "." + minorVersion + "." + buildNumber;
168+
windowsVersion = new WindowsVersion(osVersionInfo.dwMajorVersion, osVersionInfo.dwMinorVersion, osVersionInfo.dwBuildNumber);
170169
} else
171170
System.err.println("Failed to obtain OS version number (" + kernel32.GetLastError() + ")");
172171
}
173172

174-
if (versionNumber == null) {
173+
if (windowsVersion == null) {
175174
try {
176175
Process process = Runtime.getRuntime().exec(new String[]{"cmd", "ver"});
177176
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), NATIVE_CHARSET))) {
178-
Matcher matcher = Pattern.compile("(?<version>[0-9]+\\.[0-9]+\\.(?<build>[0-9]+)(\\.[0-9]+)?)]$")
177+
Matcher matcher = Pattern.compile("(?<version>\\d+\\.\\d+\\.\\d+\\.\\d+?)]$")
179178
.matcher(reader.readLine().trim());
180-
181-
if (matcher.find()) {
182-
versionNumber = matcher.group("version");
183-
buildNumber = Integer.parseInt(matcher.group("build"));
184-
}
179+
if (matcher.find())
180+
windowsVersion = new WindowsVersion(matcher.group("version"));
185181
}
186182
process.destroy();
187183
} catch (Throwable ignored) {
188184
}
189185
}
190186

191-
if (versionNumber == null)
192-
versionNumber = System.getProperty("os.version");
193-
194-
int major;
195-
int dotIndex = versionNumber.indexOf('.');
196-
try {
197-
if (dotIndex < 0)
198-
major = Integer.parseInt(versionNumber);
199-
else
200-
major = Integer.parseInt(versionNumber.substring(0, dotIndex));
201-
} catch (NumberFormatException ignored) {
202-
major = -1;
203-
}
204-
205-
// Windows XP: NT 5.1~5.2
206-
// Windows Vista: NT 6.0
207-
// Windows 7: NT 6.1
208-
209-
IS_WINDOWS_7_OR_LATER = major >= 6 && !versionNumber.startsWith("6.0");
187+
if (windowsVersion == null)
188+
windowsVersion = new WindowsVersion(System.getProperty("os.version"));
210189

211190
// Get Code Page
212191

@@ -219,9 +198,8 @@ public String getJavaExecutable() {
219198
Matcher matcher = Pattern.compile("(?<cp>[0-9]+)$")
220199
.matcher(reader.readLine().trim());
221200

222-
if (matcher.find()) {
201+
if (matcher.find())
223202
codePage = Integer.parseInt(matcher.group("cp"));
224-
}
225203
}
226204
process.destroy();
227205
} catch (Throwable ignored) {
@@ -231,19 +209,20 @@ public String getJavaExecutable() {
231209
String osName = System.getProperty("os.name");
232210

233211
// Java 17 or earlier recognizes Windows 11 as Windows 10
234-
if (osName.equals("Windows 10") && buildNumber >= 22000)
212+
if (osName.equals("Windows 10") && windowsVersion.compareTo(WindowsVersion.WINDOWS_11) >= 0)
235213
osName = "Windows 11";
236214

237215
SYSTEM_NAME = osName;
238-
SYSTEM_VERSION = versionNumber;
239-
SYSTEM_BUILD_NUMBER = buildNumber;
216+
SYSTEM_VERSION = windowsVersion.toString();
217+
SYSTEM_BUILD_NUMBER = windowsVersion.getBuild();
218+
WINDOWS_VERSION = windowsVersion;
240219
CODE_PAGE = codePage;
241220
} else {
242221
SYSTEM_NAME = System.getProperty("os.name");
243222
SYSTEM_VERSION = System.getProperty("os.version");
244223
SYSTEM_BUILD_NUMBER = -1;
224+
WINDOWS_VERSION = null;
245225
CODE_PAGE = -1;
246-
IS_WINDOWS_7_OR_LATER = false;
247226
}
248227

249228
Map<String, String> osRelease = Collections.emptyMap();
@@ -299,7 +278,7 @@ else if (name.equals("freebsd"))
299278
}
300279

301280
public static boolean isWindows7OrLater() {
302-
return IS_WINDOWS_7_OR_LATER;
281+
return WINDOWS_VERSION != null && WINDOWS_VERSION.compareTo(WindowsVersion.WINDOWS_7) >= 0;
303282
}
304283

305284
public static Path getWorkingDirectory(String folder) {
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
2+
/*
3+
* Hello Minecraft! Launcher
4+
* Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> and contributors
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
package org.jackhuang.hmcl.util.platform.windows;
20+
21+
import org.jetbrains.annotations.NotNull;
22+
23+
import java.util.Objects;
24+
import java.util.regex.Matcher;
25+
import java.util.regex.Pattern;
26+
27+
/**
28+
* @author Glavo
29+
* @see <a href="https://learn.microsoft.com/windows/win32/sysinfo/operating-system-version">Operating System Version</a>
30+
*/
31+
public final class WindowsVersion implements Comparable<WindowsVersion> {
32+
33+
public static final WindowsVersion UNKNOWN = new WindowsVersion(0, 0);
34+
35+
public static final WindowsVersion WINDOWS_2000 = new WindowsVersion(5, 0);
36+
public static final WindowsVersion WINDOWS_XP = new WindowsVersion(5, 1);
37+
public static final WindowsVersion WINDOWS_VISTA = new WindowsVersion(6, 0);
38+
public static final WindowsVersion WINDOWS_7 = new WindowsVersion(6, 1);
39+
public static final WindowsVersion WINDOWS_8 = new WindowsVersion(6, 2);
40+
public static final WindowsVersion WINDOWS_8_1 = new WindowsVersion(6, 3);
41+
public static final WindowsVersion WINDOWS_10 = new WindowsVersion(10, 0);
42+
public static final WindowsVersion WINDOWS_11 = new WindowsVersion(10, 0, 22000);
43+
44+
private final int major;
45+
private final int minor;
46+
private final int build;
47+
private final int revision;
48+
private final String version;
49+
50+
public WindowsVersion(int major, int minor) {
51+
this(major, minor, 0);
52+
}
53+
54+
public WindowsVersion(int major, int minor, int build) {
55+
this(major, minor, build, 0);
56+
}
57+
58+
public WindowsVersion(int major, int minor, int build, int revision) {
59+
this.major = major;
60+
this.minor = minor;
61+
this.build = build;
62+
this.revision = revision;
63+
64+
StringBuilder builder = new StringBuilder();
65+
builder.append(major).append('.').append(minor);
66+
if (build > 0 || revision > 0) {
67+
builder.append('.').append(build);
68+
if (revision > 0) {
69+
builder.append('.').append(revision);
70+
}
71+
}
72+
this.version = builder.toString();
73+
}
74+
75+
public WindowsVersion(@NotNull String version) {
76+
this.version = Objects.requireNonNull(version);
77+
78+
Matcher matcher = Pattern.compile("^(?<major>\\d+)\\.(?<minor>\\d+)(\\.(?<build>\\d+)(\\.(?<revision>\\d+))?)?")
79+
.matcher(version);
80+
if (matcher.find()) {
81+
this.major = Integer.parseInt(matcher.group("major"));
82+
this.minor = Integer.parseInt(matcher.group("minor"));
83+
this.build = matcher.group("build") != null ? Integer.parseInt(matcher.group("build")) : 0;
84+
this.revision = matcher.group("revision") != null ? Integer.parseInt(matcher.group("revision")) : 0;
85+
} else {
86+
this.major = 0;
87+
this.minor = 0;
88+
this.build = 0;
89+
this.revision = 0;
90+
}
91+
}
92+
93+
public int getMajor() {
94+
return major;
95+
}
96+
97+
public int getMinor() {
98+
return minor;
99+
}
100+
101+
public int getBuild() {
102+
return build;
103+
}
104+
105+
public int getRevision() {
106+
return revision;
107+
}
108+
109+
@Override
110+
public int compareTo(@NotNull WindowsVersion that) {
111+
if (this.major != that.major)
112+
return Integer.compare(this.major, that.major);
113+
if (this.minor != that.minor)
114+
return Integer.compare(this.minor, that.minor);
115+
if (this.build != that.build)
116+
return Integer.compare(this.build, that.build);
117+
return Integer.compare(this.revision, that.revision);
118+
}
119+
120+
@Override
121+
public int hashCode() {
122+
return Objects.hash(major, minor, build, revision);
123+
}
124+
125+
@Override
126+
public boolean equals(Object o) {
127+
if (this == o)
128+
return true;
129+
if (!(o instanceof WindowsVersion))
130+
return false;
131+
WindowsVersion that = (WindowsVersion) o;
132+
133+
return this.major == that.major &&
134+
this.minor == that.minor &&
135+
this.build == that.build &&
136+
this.revision == that.revision;
137+
}
138+
139+
@Override
140+
public String toString() {
141+
return version;
142+
}
143+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Hello Minecraft! Launcher
3+
* Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> and contributors
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
package org.jackhuang.hmcl.util.platform.windows;
19+
20+
import org.junit.jupiter.api.Test;
21+
22+
import static org.junit.jupiter.api.Assertions.assertEquals;
23+
24+
/**
25+
* @author Glavo
26+
*/
27+
public final class WindowsVersionTest {
28+
29+
@Test
30+
public void testParse() {
31+
assertEquals(WindowsVersion.WINDOWS_7, new WindowsVersion("6.1"));
32+
assertEquals(WindowsVersion.WINDOWS_10, new WindowsVersion("10.0"));
33+
assertEquals(new WindowsVersion(10, 0, 26120), new WindowsVersion("10.0.26120"));
34+
assertEquals(new WindowsVersion(10, 0, 26120), new WindowsVersion("10.0.26120-unknown"));
35+
assertEquals(new WindowsVersion(10, 0, 26120, 3964), new WindowsVersion("10.0.26120.3964"));
36+
assertEquals(new WindowsVersion(10, 0, 26120, 3964), new WindowsVersion("10.0.26120.3964-unknown"));
37+
38+
assertEquals(WindowsVersion.UNKNOWN, new WindowsVersion("unknown"));
39+
}
40+
}

0 commit comments

Comments
 (0)