mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-07 19:11:48 -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 org.jackhuang.hmcl.util.platform.NativeUtils;
|
||||||
|
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -175,6 +176,26 @@ public abstract class WinReg {
|
|||||||
|
|
||||||
return lpData.getWideString(0L);
|
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:
|
default:
|
||||||
throw new RuntimeException("Unknown reg type: " + type);
|
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;
|
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.NativeUtils;
|
||||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
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.CentralProcessor;
|
||||||
import org.jackhuang.hmcl.util.platform.hardware.GraphicsCard;
|
import org.jackhuang.hmcl.util.platform.hardware.GraphicsCard;
|
||||||
import org.jackhuang.hmcl.util.platform.hardware.HardwareDetector;
|
import org.jackhuang.hmcl.util.platform.hardware.HardwareDetector;
|
||||||
import org.jackhuang.hmcl.util.platform.hardware.HardwareVendor;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
@ -50,49 +44,7 @@ public final class WindowsHardwareDetector extends HardwareDetector {
|
|||||||
public List<GraphicsCard> detectGraphicsCards() {
|
public List<GraphicsCard> detectGraphicsCards() {
|
||||||
if (!OperatingSystem.isWindows7OrLater())
|
if (!OperatingSystem.isWindows7OrLater())
|
||||||
return null;
|
return null;
|
||||||
|
return WindowsGPUDetector.detect();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -23,10 +23,10 @@ import org.junit.jupiter.api.BeforeAll;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.EnabledIf;
|
import org.junit.jupiter.api.condition.EnabledIf;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
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 byte[] TEST_DATA = new byte[128];
|
||||||
|
private static final String[] TEST_STRING_ARRAY = {
|
||||||
|
"str0", "str1", "str2"
|
||||||
|
};
|
||||||
|
|
||||||
static {
|
static {
|
||||||
for (int i = 0; i < TEST_DATA.length; i++) {
|
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.registrySetStringValue(ROOT_KEY, key, "SZ", "Hello World!");
|
||||||
Advapi32Util.registrySetIntValue(ROOT_KEY, key, "DWORD", 0xCAFEBABE);
|
Advapi32Util.registrySetIntValue(ROOT_KEY, key, "DWORD", 0xCAFEBABE);
|
||||||
Advapi32Util.registrySetLongValue(ROOT_KEY, key, "QWORD", 0xCAFEBABEL);
|
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) {
|
for (String subkey : SUBKEYS) {
|
||||||
if (!Advapi32Util.registryCreateKey(ROOT_KEY, key, subkey))
|
if (!Advapi32Util.registryCreateKey(ROOT_KEY, key, subkey))
|
||||||
@ -94,6 +99,9 @@ public final class WinRegTest {
|
|||||||
assertEquals("Hello World!", reg.queryValue(hkey, key, "SZ"));
|
assertEquals("Hello World!", reg.queryValue(hkey, key, "SZ"));
|
||||||
assertEquals(0xCAFEBABE, reg.queryValue(hkey, key, "DWORD"));
|
assertEquals(0xCAFEBABE, reg.queryValue(hkey, key, "DWORD"));
|
||||||
assertEquals(0xCAFEBABEL, reg.queryValue(hkey, key, "QWORD"));
|
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, "UNKNOWN"));
|
||||||
assertNull(reg.queryValue(hkey, KEY_BASE + "\\" + "NOT_EXIST", "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.HKEY hkey = WinReg.HKEY.HKEY_CURRENT_USER;
|
||||||
WinReg reg = WinReg.INSTANCE;
|
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()));
|
reg.querySubKeys(hkey, key).stream().sorted().collect(Collectors.toList()));
|
||||||
for (String subkey : SUBKEYS) {
|
for (String subkey : SUBKEYS) {
|
||||||
assertEquals(Collections.emptyList(), reg.querySubKeys(hkey, key + "\\" + subkey));
|
assertEquals(Collections.emptyList(), reg.querySubKeys(hkey, key + "\\" + subkey));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user