mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-08-03 11:26:38 -04:00
在 Windows 平台优先使用注册表探测 GPU (#4145)
This commit is contained in:
parent
3b17e0a897
commit
329b0e8f3a
@ -25,6 +25,7 @@ import com.sun.jna.ptr.PointerByReference;
|
||||
import org.jackhuang.hmcl.util.platform.NativeUtils;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -175,6 +176,26 @@ public abstract class WinReg {
|
||||
|
||||
return lpData.getWideString(0L);
|
||||
}
|
||||
case WinConstants.REG_MULTI_SZ: {
|
||||
String str = new String(lpData.getByteArray(0L, cbData), StandardCharsets.UTF_16LE);
|
||||
|
||||
var result = new ArrayList<String>();
|
||||
for (int start = 0; start < str.length(); ) {
|
||||
final int end = str.indexOf('\0', start);
|
||||
if (end < 0) {
|
||||
result.add(str.substring(start));
|
||||
break;
|
||||
} else if (end == start && end == str.length() - 1) {
|
||||
// The terminator of the array
|
||||
break;
|
||||
}
|
||||
|
||||
result.add(str.substring(start, end));
|
||||
start = end + 1;
|
||||
}
|
||||
|
||||
return result.toArray(new String[0]);
|
||||
}
|
||||
default:
|
||||
throw new RuntimeException("Unknown reg type: " + type);
|
||||
}
|
||||
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.platform.windows;
|
||||
|
||||
import org.jackhuang.hmcl.util.KeyValuePairUtils;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.SystemUtils;
|
||||
import org.jackhuang.hmcl.util.platform.hardware.GraphicsCard;
|
||||
import org.jackhuang.hmcl.util.platform.hardware.HardwareVendor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
final class WindowsGPUDetector {
|
||||
|
||||
private static GraphicsCard.Type fromDacType(String adapterDACType) {
|
||||
if (StringUtils.isBlank(adapterDACType)
|
||||
|| "Internal".equalsIgnoreCase(adapterDACType)
|
||||
|| "InternalDAC".equalsIgnoreCase(adapterDACType)) {
|
||||
return GraphicsCard.Type.Integrated;
|
||||
} else {
|
||||
return GraphicsCard.Type.Discrete;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<GraphicsCard> detectByCim() {
|
||||
try {
|
||||
String getCimInstance = OperatingSystem.SYSTEM_VERSION.startsWith("6.1")
|
||||
? "Get-WmiObject"
|
||||
: "Get-CimInstance";
|
||||
|
||||
List<Map<String, String>> videoControllers = SystemUtils.run(Arrays.asList(
|
||||
"powershell.exe",
|
||||
"-NoProfile",
|
||||
"-Command",
|
||||
String.join(" | ",
|
||||
getCimInstance + " -Class Win32_VideoController",
|
||||
"Select-Object Name,AdapterCompatibility,DriverVersion,AdapterDACType",
|
||||
"Format-List"
|
||||
)),
|
||||
inputStream -> KeyValuePairUtils.loadList(new BufferedReader(new InputStreamReader(inputStream, OperatingSystem.NATIVE_CHARSET))));
|
||||
|
||||
ArrayList<GraphicsCard> cards = new ArrayList<>(videoControllers.size());
|
||||
for (Map<String, String> videoController : videoControllers) {
|
||||
String name = videoController.get("Name");
|
||||
String adapterCompatibility = videoController.get("AdapterCompatibility");
|
||||
String driverVersion = videoController.get("DriverVersion");
|
||||
String adapterDACType = videoController.get("AdapterDACType");
|
||||
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
cards.add(GraphicsCard.builder().setName(GraphicsCard.cleanName(name))
|
||||
.setVendor(HardwareVendor.of(adapterCompatibility))
|
||||
.setDriverVersion(driverVersion)
|
||||
.setType(fromDacType(adapterDACType))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return cards;
|
||||
} catch (Throwable e) {
|
||||
LOG.warning("Failed to get graphics card info", e);
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable String regValueToString(Object object) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
} else if (object instanceof String[]) {
|
||||
return String.join(" ", (String[]) object);
|
||||
} else {
|
||||
return object.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<GraphicsCard> detectByRegistry(WinReg reg) {
|
||||
final WinReg.HKEY hkey = WinReg.HKEY.HKEY_LOCAL_MACHINE;
|
||||
final String displayDevices = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\";
|
||||
final Pattern graphicsCardPattern = Pattern.compile("\\\\[0-9]+\\\\?$");
|
||||
|
||||
var result = new ArrayList<GraphicsCard>();
|
||||
for (String subkey : reg.querySubKeys(hkey, displayDevices)) {
|
||||
if (!graphicsCardPattern.matcher(subkey).find())
|
||||
continue;
|
||||
|
||||
String name = regValueToString(reg.queryValue(hkey, subkey, "HardwareInformation.AdapterString"));
|
||||
String vendor = regValueToString(reg.queryValue(hkey, subkey, "ProviderName"));
|
||||
String driverVersion = regValueToString(reg.queryValue(hkey, subkey, "DriverVersion"));
|
||||
String dacType = regValueToString(reg.queryValue(hkey, subkey, "HardwareInformation.DacType"));
|
||||
|
||||
GraphicsCard.Builder builder = GraphicsCard.builder();
|
||||
if (name != null)
|
||||
builder.setName(GraphicsCard.cleanName(name));
|
||||
if (vendor != null)
|
||||
builder.setVendor(HardwareVendor.of(vendor));
|
||||
if (driverVersion != null)
|
||||
builder.setDriverVersion(driverVersion);
|
||||
if (dacType != null)
|
||||
builder.setType(fromDacType(dacType));
|
||||
result.add(builder.build());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static @Nullable List<GraphicsCard> detect() {
|
||||
try {
|
||||
WinReg reg = WinReg.INSTANCE;
|
||||
if (reg != null) {
|
||||
List<GraphicsCard> res = detectByRegistry(reg);
|
||||
if (!res.isEmpty())
|
||||
return res;
|
||||
}
|
||||
return detectByCim();
|
||||
} catch (Throwable e) {
|
||||
LOG.warning("Failed to get graphics cards", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private WindowsGPUDetector() {
|
||||
}
|
||||
}
|
@ -17,19 +17,13 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.platform.windows;
|
||||
|
||||
import org.jackhuang.hmcl.util.KeyValuePairUtils;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.platform.NativeUtils;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.SystemUtils;
|
||||
import org.jackhuang.hmcl.util.platform.hardware.CentralProcessor;
|
||||
import org.jackhuang.hmcl.util.platform.hardware.GraphicsCard;
|
||||
import org.jackhuang.hmcl.util.platform.hardware.HardwareDetector;
|
||||
import org.jackhuang.hmcl.util.platform.hardware.HardwareVendor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.*;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
@ -50,49 +44,7 @@ public final class WindowsHardwareDetector extends HardwareDetector {
|
||||
public List<GraphicsCard> detectGraphicsCards() {
|
||||
if (!OperatingSystem.isWindows7OrLater())
|
||||
return null;
|
||||
|
||||
try {
|
||||
String getCimInstance = OperatingSystem.SYSTEM_VERSION.startsWith("6.1")
|
||||
? "Get-WmiObject"
|
||||
: "Get-CimInstance";
|
||||
|
||||
List<Map<String, String>> videoControllers = SystemUtils.run(Arrays.asList(
|
||||
"powershell.exe",
|
||||
"-NoProfile",
|
||||
"-Command",
|
||||
String.join(" | ",
|
||||
getCimInstance + " -Class Win32_VideoController",
|
||||
"Select-Object Name,AdapterCompatibility,DriverVersion,AdapterDACType",
|
||||
"Format-List"
|
||||
)),
|
||||
inputStream -> KeyValuePairUtils.loadList(new BufferedReader(new InputStreamReader(inputStream, OperatingSystem.NATIVE_CHARSET))));
|
||||
|
||||
ArrayList<GraphicsCard> cards = new ArrayList<>(videoControllers.size());
|
||||
for (Map<String, String> videoController : videoControllers) {
|
||||
String name = videoController.get("Name");
|
||||
String adapterCompatibility = videoController.get("AdapterCompatibility");
|
||||
String driverVersion = videoController.get("DriverVersion");
|
||||
String adapterDACType = videoController.get("AdapterDACType");
|
||||
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
cards.add(GraphicsCard.builder().setName(GraphicsCard.cleanName(name))
|
||||
.setVendor(HardwareVendor.of(adapterCompatibility))
|
||||
.setDriverVersion(driverVersion)
|
||||
.setType(StringUtils.isBlank(adapterDACType)
|
||||
|| "Internal".equalsIgnoreCase(adapterDACType)
|
||||
|| "InternalDAC".equalsIgnoreCase(adapterDACType)
|
||||
? GraphicsCard.Type.Integrated
|
||||
: GraphicsCard.Type.Discrete)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return cards;
|
||||
} catch (Throwable e) {
|
||||
LOG.warning("Failed to get graphics card info", e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return WindowsGPUDetector.detect();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -23,10 +23,10 @@ import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIf;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ -48,6 +48,9 @@ public final class WinRegTest {
|
||||
};
|
||||
|
||||
private static final byte[] TEST_DATA = new byte[128];
|
||||
private static final String[] TEST_STRING_ARRAY = {
|
||||
"str0", "str1", "str2"
|
||||
};
|
||||
|
||||
static {
|
||||
for (int i = 0; i < TEST_DATA.length; i++) {
|
||||
@ -65,6 +68,8 @@ public final class WinRegTest {
|
||||
Advapi32Util.registrySetStringValue(ROOT_KEY, key, "SZ", "Hello World!");
|
||||
Advapi32Util.registrySetIntValue(ROOT_KEY, key, "DWORD", 0xCAFEBABE);
|
||||
Advapi32Util.registrySetLongValue(ROOT_KEY, key, "QWORD", 0xCAFEBABEL);
|
||||
Advapi32Util.registrySetStringArray(ROOT_KEY, key, "REG_MULTI_SZ", TEST_STRING_ARRAY);
|
||||
Advapi32Util.registrySetStringArray(ROOT_KEY, key, "REG_MULTI_SZ_EMPTY", new String[0]);
|
||||
|
||||
for (String subkey : SUBKEYS) {
|
||||
if (!Advapi32Util.registryCreateKey(ROOT_KEY, key, subkey))
|
||||
@ -94,6 +99,9 @@ public final class WinRegTest {
|
||||
assertEquals("Hello World!", reg.queryValue(hkey, key, "SZ"));
|
||||
assertEquals(0xCAFEBABE, reg.queryValue(hkey, key, "DWORD"));
|
||||
assertEquals(0xCAFEBABEL, reg.queryValue(hkey, key, "QWORD"));
|
||||
assertArrayEquals(TEST_STRING_ARRAY, (Object[]) reg.queryValue(hkey, key, "REG_MULTI_SZ"));
|
||||
assertArrayEquals(new String[0], (Object[]) reg.queryValue(hkey, key, "REG_MULTI_SZ_EMPTY"));
|
||||
|
||||
assertNull(reg.queryValue(hkey, key, "UNKNOWN"));
|
||||
assertNull(reg.queryValue(hkey, KEY_BASE + "\\" + "NOT_EXIST", "UNKNOWN"));
|
||||
}
|
||||
@ -103,7 +111,7 @@ public final class WinRegTest {
|
||||
WinReg.HKEY hkey = WinReg.HKEY.HKEY_CURRENT_USER;
|
||||
WinReg reg = WinReg.INSTANCE;
|
||||
|
||||
assertEquals(Arrays.asList(SUBKEYS).stream().map(it -> key + "\\" + it).collect(Collectors.toList()),
|
||||
assertEquals(Stream.of(SUBKEYS).map(it -> key + "\\" + it).collect(Collectors.toList()),
|
||||
reg.querySubKeys(hkey, key).stream().sorted().collect(Collectors.toList()));
|
||||
for (String subkey : SUBKEYS) {
|
||||
assertEquals(Collections.emptyList(), reg.querySubKeys(hkey, key + "\\" + subkey));
|
||||
|
Loading…
x
Reference in New Issue
Block a user