From 58a4700a466d16b78c68004e738c7653edbf6a52 Mon Sep 17 00:00:00 2001 From: Glavo Date: Fri, 23 May 2025 21:11:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9B=E5=BB=BA=20WindowsVersion=20=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E7=B1=BB=20(#3932)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hmcl/util/platform/OperatingSystem.java | 65 +++----- .../util/platform/windows/WindowsVersion.java | 143 ++++++++++++++++++ .../platform/windows/WindowsVersionTest.java | 40 +++++ 3 files changed, 205 insertions(+), 43 deletions(-) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WindowsVersion.java create mode 100644 HMCLCore/src/test/java/org/jackhuang/hmcl/util/platform/windows/WindowsVersionTest.java diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/OperatingSystem.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/OperatingSystem.java index 918aef96e..f89493d1f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/OperatingSystem.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/OperatingSystem.java @@ -20,6 +20,8 @@ package org.jackhuang.hmcl.util.platform; import org.jackhuang.hmcl.util.KeyValuePairUtils; import org.jackhuang.hmcl.util.platform.windows.Kernel32; import org.jackhuang.hmcl.util.platform.windows.WinTypes; +import org.jackhuang.hmcl.util.platform.windows.WindowsVersion; +import org.jetbrains.annotations.Nullable; import java.io.BufferedReader; import java.io.IOException; @@ -106,6 +108,11 @@ public enum OperatingSystem { */ public static final int SYSTEM_BUILD_NUMBER; + /** + * The version number is non-null if and only if the operating system is {@linkplain #WINDOWS}. + */ + public static final @Nullable WindowsVersion WINDOWS_VERSION; + /** * The name of current operating system. */ @@ -125,8 +132,6 @@ public enum OperatingSystem { private static final String[] INVALID_RESOURCE_BASENAMES; private static final String[] INVALID_RESOURCE_FULLNAMES; - private static final boolean IS_WINDOWS_7_OR_LATER; - static { String nativeEncoding = System.getProperty("native.encoding"); String hmclNativeEncoding = System.getProperty("hmcl.native.encoding"); @@ -152,61 +157,35 @@ public enum OperatingSystem { NATIVE_CHARSET = nativeCharset; if (CURRENT_OS == WINDOWS) { - String versionNumber = null; - int buildNumber = -1; int codePage = -1; + WindowsVersion windowsVersion = null; Kernel32 kernel32 = Kernel32.INSTANCE; - // Get Windows version number if (kernel32 != null) { WinTypes.OSVERSIONINFOEXW osVersionInfo = new WinTypes.OSVERSIONINFOEXW(); if (kernel32.GetVersionExW(osVersionInfo)) { - int majorVersion = osVersionInfo.dwMajorVersion; - int minorVersion = osVersionInfo.dwMinorVersion; - - buildNumber = osVersionInfo.dwBuildNumber; - versionNumber = majorVersion + "." + minorVersion + "." + buildNumber; + windowsVersion = new WindowsVersion(osVersionInfo.dwMajorVersion, osVersionInfo.dwMinorVersion, osVersionInfo.dwBuildNumber); } else System.err.println("Failed to obtain OS version number (" + kernel32.GetLastError() + ")"); } - if (versionNumber == null) { + if (windowsVersion == null) { try { Process process = Runtime.getRuntime().exec(new String[]{"cmd", "ver"}); try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), NATIVE_CHARSET))) { - Matcher matcher = Pattern.compile("(?[0-9]+\\.[0-9]+\\.(?[0-9]+)(\\.[0-9]+)?)]$") + Matcher matcher = Pattern.compile("(?\\d+\\.\\d+\\.\\d+\\.\\d+?)]$") .matcher(reader.readLine().trim()); - - if (matcher.find()) { - versionNumber = matcher.group("version"); - buildNumber = Integer.parseInt(matcher.group("build")); - } + if (matcher.find()) + windowsVersion = new WindowsVersion(matcher.group("version")); } process.destroy(); } catch (Throwable ignored) { } } - if (versionNumber == null) - versionNumber = System.getProperty("os.version"); - - int major; - int dotIndex = versionNumber.indexOf('.'); - try { - if (dotIndex < 0) - major = Integer.parseInt(versionNumber); - else - major = Integer.parseInt(versionNumber.substring(0, dotIndex)); - } catch (NumberFormatException ignored) { - major = -1; - } - - // Windows XP: NT 5.1~5.2 - // Windows Vista: NT 6.0 - // Windows 7: NT 6.1 - - IS_WINDOWS_7_OR_LATER = major >= 6 && !versionNumber.startsWith("6.0"); + if (windowsVersion == null) + windowsVersion = new WindowsVersion(System.getProperty("os.version")); // Get Code Page @@ -219,9 +198,8 @@ public enum OperatingSystem { Matcher matcher = Pattern.compile("(?[0-9]+)$") .matcher(reader.readLine().trim()); - if (matcher.find()) { + if (matcher.find()) codePage = Integer.parseInt(matcher.group("cp")); - } } process.destroy(); } catch (Throwable ignored) { @@ -231,19 +209,20 @@ public enum OperatingSystem { String osName = System.getProperty("os.name"); // Java 17 or earlier recognizes Windows 11 as Windows 10 - if (osName.equals("Windows 10") && buildNumber >= 22000) + if (osName.equals("Windows 10") && windowsVersion.compareTo(WindowsVersion.WINDOWS_11) >= 0) osName = "Windows 11"; SYSTEM_NAME = osName; - SYSTEM_VERSION = versionNumber; - SYSTEM_BUILD_NUMBER = buildNumber; + SYSTEM_VERSION = windowsVersion.toString(); + SYSTEM_BUILD_NUMBER = windowsVersion.getBuild(); + WINDOWS_VERSION = windowsVersion; CODE_PAGE = codePage; } else { SYSTEM_NAME = System.getProperty("os.name"); SYSTEM_VERSION = System.getProperty("os.version"); SYSTEM_BUILD_NUMBER = -1; + WINDOWS_VERSION = null; CODE_PAGE = -1; - IS_WINDOWS_7_OR_LATER = false; } Map osRelease = Collections.emptyMap(); @@ -299,7 +278,7 @@ public enum OperatingSystem { } public static boolean isWindows7OrLater() { - return IS_WINDOWS_7_OR_LATER; + return WINDOWS_VERSION != null && WINDOWS_VERSION.compareTo(WindowsVersion.WINDOWS_7) >= 0; } public static Path getWorkingDirectory(String folder) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WindowsVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WindowsVersion.java new file mode 100644 index 000000000..c980d6ce6 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WindowsVersion.java @@ -0,0 +1,143 @@ + +/* + * Hello Minecraft! Launcher + * Copyright (C) 2025 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.util.platform.windows; + +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Glavo + * @see Operating System Version + */ +public final class WindowsVersion implements Comparable { + + public static final WindowsVersion UNKNOWN = new WindowsVersion(0, 0); + + public static final WindowsVersion WINDOWS_2000 = new WindowsVersion(5, 0); + public static final WindowsVersion WINDOWS_XP = new WindowsVersion(5, 1); + public static final WindowsVersion WINDOWS_VISTA = new WindowsVersion(6, 0); + public static final WindowsVersion WINDOWS_7 = new WindowsVersion(6, 1); + public static final WindowsVersion WINDOWS_8 = new WindowsVersion(6, 2); + public static final WindowsVersion WINDOWS_8_1 = new WindowsVersion(6, 3); + public static final WindowsVersion WINDOWS_10 = new WindowsVersion(10, 0); + public static final WindowsVersion WINDOWS_11 = new WindowsVersion(10, 0, 22000); + + private final int major; + private final int minor; + private final int build; + private final int revision; + private final String version; + + public WindowsVersion(int major, int minor) { + this(major, minor, 0); + } + + public WindowsVersion(int major, int minor, int build) { + this(major, minor, build, 0); + } + + public WindowsVersion(int major, int minor, int build, int revision) { + this.major = major; + this.minor = minor; + this.build = build; + this.revision = revision; + + StringBuilder builder = new StringBuilder(); + builder.append(major).append('.').append(minor); + if (build > 0 || revision > 0) { + builder.append('.').append(build); + if (revision > 0) { + builder.append('.').append(revision); + } + } + this.version = builder.toString(); + } + + public WindowsVersion(@NotNull String version) { + this.version = Objects.requireNonNull(version); + + Matcher matcher = Pattern.compile("^(?\\d+)\\.(?\\d+)(\\.(?\\d+)(\\.(?\\d+))?)?") + .matcher(version); + if (matcher.find()) { + this.major = Integer.parseInt(matcher.group("major")); + this.minor = Integer.parseInt(matcher.group("minor")); + this.build = matcher.group("build") != null ? Integer.parseInt(matcher.group("build")) : 0; + this.revision = matcher.group("revision") != null ? Integer.parseInt(matcher.group("revision")) : 0; + } else { + this.major = 0; + this.minor = 0; + this.build = 0; + this.revision = 0; + } + } + + public int getMajor() { + return major; + } + + public int getMinor() { + return minor; + } + + public int getBuild() { + return build; + } + + public int getRevision() { + return revision; + } + + @Override + public int compareTo(@NotNull WindowsVersion that) { + if (this.major != that.major) + return Integer.compare(this.major, that.major); + if (this.minor != that.minor) + return Integer.compare(this.minor, that.minor); + if (this.build != that.build) + return Integer.compare(this.build, that.build); + return Integer.compare(this.revision, that.revision); + } + + @Override + public int hashCode() { + return Objects.hash(major, minor, build, revision); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof WindowsVersion)) + return false; + WindowsVersion that = (WindowsVersion) o; + + return this.major == that.major && + this.minor == that.minor && + this.build == that.build && + this.revision == that.revision; + } + + @Override + public String toString() { + return version; + } +} diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/platform/windows/WindowsVersionTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/platform/windows/WindowsVersionTest.java new file mode 100644 index 000000000..de938ae42 --- /dev/null +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/platform/windows/WindowsVersionTest.java @@ -0,0 +1,40 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2025 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.util.platform.windows; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Glavo + */ +public final class WindowsVersionTest { + + @Test + public void testParse() { + assertEquals(WindowsVersion.WINDOWS_7, new WindowsVersion("6.1")); + assertEquals(WindowsVersion.WINDOWS_10, new WindowsVersion("10.0")); + assertEquals(new WindowsVersion(10, 0, 26120), new WindowsVersion("10.0.26120")); + assertEquals(new WindowsVersion(10, 0, 26120), new WindowsVersion("10.0.26120-unknown")); + assertEquals(new WindowsVersion(10, 0, 26120, 3964), new WindowsVersion("10.0.26120.3964")); + assertEquals(new WindowsVersion(10, 0, 26120, 3964), new WindowsVersion("10.0.26120.3964-unknown")); + + assertEquals(WindowsVersion.UNKNOWN, new WindowsVersion("unknown")); + } +}