mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-10 12:26:16 -04:00
使用 JNA 查询注册表 (#3913)
This commit is contained in:
parent
76ed9353bd
commit
6e05b5ee58
@ -31,18 +31,17 @@ import org.jackhuang.hmcl.task.Task;
|
|||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.util.CacheRepository;
|
import org.jackhuang.hmcl.util.CacheRepository;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||||
import org.jackhuang.hmcl.util.platform.Platform;
|
import org.jackhuang.hmcl.util.platform.Platform;
|
||||||
import org.jackhuang.hmcl.util.platform.UnsupportedPlatformException;
|
import org.jackhuang.hmcl.util.platform.UnsupportedPlatformException;
|
||||||
|
import org.jackhuang.hmcl.util.platform.windows.WinReg;
|
||||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
@ -357,10 +356,10 @@ public final class JavaManager {
|
|||||||
|
|
||||||
switch (OperatingSystem.CURRENT_OS) {
|
switch (OperatingSystem.CURRENT_OS) {
|
||||||
case WINDOWS:
|
case WINDOWS:
|
||||||
queryJavaInRegistryKey(javaRuntimes, "HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Runtime Environment\\");
|
queryJavaInRegistryKey(javaRuntimes, WinReg.HKEY.HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
|
||||||
queryJavaInRegistryKey(javaRuntimes, "HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\");
|
queryJavaInRegistryKey(javaRuntimes, WinReg.HKEY.HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\Java Development Kit");
|
||||||
queryJavaInRegistryKey(javaRuntimes, "HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\JRE\\");
|
queryJavaInRegistryKey(javaRuntimes, WinReg.HKEY.HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\JRE");
|
||||||
queryJavaInRegistryKey(javaRuntimes, "HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\JDK\\");
|
queryJavaInRegistryKey(javaRuntimes, WinReg.HKEY.HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\JDK");
|
||||||
|
|
||||||
searchJavaInProgramFiles(javaRuntimes, "ProgramFiles", "C:\\Program Files");
|
searchJavaInProgramFiles(javaRuntimes, "ProgramFiles", "C:\\Program Files");
|
||||||
searchJavaInProgramFiles(javaRuntimes, "ProgramFiles(x86)", "C:\\Program Files (x86)");
|
searchJavaInProgramFiles(javaRuntimes, "ProgramFiles(x86)", "C:\\Program Files (x86)");
|
||||||
@ -646,67 +645,22 @@ public final class JavaManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==== Windows Registry Support ====
|
// ==== Windows Registry Support ====
|
||||||
private static void queryJavaInRegistryKey(Map<Path, JavaRuntime> javaRuntimes, String location) {
|
private static void queryJavaInRegistryKey(Map<Path, JavaRuntime> javaRuntimes, WinReg.HKEY hkey, String location) {
|
||||||
for (String java : querySubFolders(location)) {
|
WinReg reg = WinReg.INSTANCE;
|
||||||
if (!querySubFolders(java).contains(java + "\\MSI"))
|
if (reg == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (String java : reg.queryKeys(hkey, location)) {
|
||||||
|
if (!reg.queryKeys(hkey, java).contains(java + "\\MSI"))
|
||||||
continue;
|
continue;
|
||||||
String home = queryRegisterValue(java, "JavaHome");
|
Object home = reg.queryValue(hkey, java, "JavaHome");
|
||||||
if (home != null) {
|
if (home instanceof String) {
|
||||||
try {
|
try {
|
||||||
tryAddJavaHome(javaRuntimes, Paths.get(home));
|
tryAddJavaHome(javaRuntimes, Paths.get((String) home));
|
||||||
} catch (InvalidPathException e) {
|
} catch (InvalidPathException e) {
|
||||||
LOG.warning("Invalid Java path in system registry: " + home);
|
LOG.warning("Invalid Java path in system registry: " + home);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<String> querySubFolders(String location) {
|
|
||||||
List<String> res = new ArrayList<>();
|
|
||||||
|
|
||||||
try {
|
|
||||||
Process process = Runtime.getRuntime().exec(new String[]{"cmd", "/c", "reg", "query", location});
|
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), OperatingSystem.NATIVE_CHARSET))) {
|
|
||||||
for (String line; (line = reader.readLine()) != null; ) {
|
|
||||||
if (line.startsWith(location) && !line.equals(location)) {
|
|
||||||
res.add(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOG.warning("Failed to query sub folders of " + location, e);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String queryRegisterValue(String location, String name) {
|
|
||||||
boolean last = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Process process = Runtime.getRuntime().exec(new String[]{"cmd", "/c", "reg", "query", location, "/v", name});
|
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), OperatingSystem.NATIVE_CHARSET))) {
|
|
||||||
for (String line; (line = reader.readLine()) != null; ) {
|
|
||||||
if (StringUtils.isNotBlank(line)) {
|
|
||||||
if (last && line.trim().startsWith(name)) {
|
|
||||||
int begins = line.indexOf(name);
|
|
||||||
if (begins > 0) {
|
|
||||||
String s2 = line.substring(begins + name.length());
|
|
||||||
begins = s2.indexOf("REG_SZ");
|
|
||||||
if (begins > 0) {
|
|
||||||
return s2.substring(begins + "REG_SZ".length()).trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (location.equals(line.trim())) {
|
|
||||||
last = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOG.warning("Failed to query register value of " + location, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -19,4 +19,6 @@ dependencies {
|
|||||||
api(libs.pci.ids)
|
api(libs.pci.ids)
|
||||||
|
|
||||||
compileOnlyApi(libs.jetbrains.annotations)
|
compileOnlyApi(libs.jetbrains.annotations)
|
||||||
|
|
||||||
|
testImplementation(libs.jna.platform)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.sun.jna.Pointer;
|
||||||
|
import com.sun.jna.WString;
|
||||||
|
import com.sun.jna.ptr.IntByReference;
|
||||||
|
import com.sun.jna.ptr.PointerByReference;
|
||||||
|
import com.sun.jna.win32.StdCallLibrary;
|
||||||
|
import org.jackhuang.hmcl.util.platform.NativeUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Glavo
|
||||||
|
*/
|
||||||
|
public interface Advapi32 extends StdCallLibrary {
|
||||||
|
|
||||||
|
Advapi32 INSTANCE = NativeUtils.USE_JNA && com.sun.jna.Platform.isWindows()
|
||||||
|
? NativeUtils.load("advapi32", Advapi32.class)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see <a href="https://learn.microsoft.com/windows/win32/api/winreg/nf-winreg-regopenkeyexw">RegOpenKeyExW function</a>
|
||||||
|
*/
|
||||||
|
int RegOpenKeyExW(Pointer hKey, WString lpSubKey, int ulOptions, int samDesired, PointerByReference phkResult);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see <a href="https://learn.microsoft.com/windows/win32/api/winreg/nf-winreg-regclosekey">RegCloseKey function</a>
|
||||||
|
*/
|
||||||
|
int RegCloseKey(Pointer hKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see <a href="https://learn.microsoft.com/windows/win32/api/winreg/nf-winreg-regqueryvalueexw">RegQueryValueExW function</a>
|
||||||
|
*/
|
||||||
|
int RegQueryValueExW(Pointer hKey, WString lpValueName, Pointer lpReserved, IntByReference lpType, Pointer lpData, IntByReference lpcbData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see <a href="https://learn.microsoft.com/windows/win32/api/winreg/nf-winreg-regenumkeyexw">RegEnumKeyExW function</a>
|
||||||
|
*/
|
||||||
|
int RegEnumKeyExW(Pointer hKey, int dwIndex,
|
||||||
|
Pointer lpName, IntByReference lpcchName,
|
||||||
|
IntByReference lpReserved,
|
||||||
|
Pointer lpClass, IntByReference lpcchClass,
|
||||||
|
Pointer lpftLastWriteTime);
|
||||||
|
}
|
@ -22,9 +22,53 @@ package org.jackhuang.hmcl.util.platform.windows;
|
|||||||
*/
|
*/
|
||||||
public interface WinConstants {
|
public interface WinConstants {
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/windows/win32/debug/system-error-codes--0-499-
|
||||||
|
int ERROR_SUCCESS = 0;
|
||||||
|
int ERROR_FILE_NOT_FOUND = 2;
|
||||||
|
int ERROR_PATH_NOT_FOUND = 3;
|
||||||
|
int ERROR_ACCESS_DENIED = 5;
|
||||||
|
int ERROR_INVALID_HANDLE = 6;
|
||||||
|
int ERROR_INVALID_DATA = 13;
|
||||||
|
int ERROR_NOT_SAME_DEVICE = 17;
|
||||||
|
int ERROR_NOT_READY = 21;
|
||||||
|
int ERROR_SHARING_VIOLATION = 32;
|
||||||
|
int ERROR_FILE_EXISTS = 80;
|
||||||
|
int ERROR_INVALID_PARAMETER = 87;
|
||||||
|
int ERROR_DISK_FULL = 112;
|
||||||
|
int ERROR_INSUFFICIENT_BUFFER = 122;
|
||||||
|
int ERROR_INVALID_LEVEL = 124;
|
||||||
|
int ERROR_DIR_NOT_ROOT = 144;
|
||||||
|
int ERROR_DIR_NOT_EMPTY = 145;
|
||||||
|
int ERROR_ALREADY_EXISTS = 183;
|
||||||
|
int ERROR_MORE_DATA = 234;
|
||||||
|
int ERROR_NO_MORE_ITEMS = 259;
|
||||||
|
int ERROR_DIRECTORY = 267;
|
||||||
|
int ERROR_NOTIFY_ENUM_DIR = 1022;
|
||||||
|
int ERROR_PRIVILEGE_NOT_HELD = 1314;
|
||||||
|
int ERROR_NONE_MAPPED = 1332;
|
||||||
|
int ERROR_CANT_ACCESS_FILE = 1920;
|
||||||
|
int ERROR_NOT_A_REPARSE_POINT = 4390;
|
||||||
|
int ERROR_INVALID_REPARSE_DATA = 4392;
|
||||||
|
|
||||||
// https://learn.microsoft.com/windows/win32/sysinfo/registry-key-security-and-access-rights
|
// https://learn.microsoft.com/windows/win32/sysinfo/registry-key-security-and-access-rights
|
||||||
|
int KEY_QUERY_VALUE = 0x0001;
|
||||||
|
int KEY_ENUMERATE_SUB_KEYS = 0x0008;
|
||||||
int KEY_READ = 0x20019;
|
int KEY_READ = 0x20019;
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/windows/win32/sysinfo/registry-value-types
|
||||||
|
int REG_NONE = 0;
|
||||||
|
int REG_SZ = 1;
|
||||||
|
int REG_EXPAND_SZ = 2;
|
||||||
|
int REG_BINARY = 3;
|
||||||
|
int REG_DWORD_LITTLE_ENDIAN = 4;
|
||||||
|
int REG_DWORD_BIG_ENDIAN = 5;
|
||||||
|
int REG_LINK = 6;
|
||||||
|
int REG_MULTI_SZ = 7;
|
||||||
|
int REG_RESOURCE_LIST = 8;
|
||||||
|
int REG_FULL_RESOURCE_DESCRIPTOR = 9;
|
||||||
|
int REG_RESOURCE_REQUIREMENTS_LIST = 10;
|
||||||
|
int REG_QWORD_LITTLE_ENDIAN = 11;
|
||||||
|
|
||||||
// https://learn.microsoft.com/windows/win32/sysinfo/predefined-keys
|
// https://learn.microsoft.com/windows/win32/sysinfo/predefined-keys
|
||||||
long HKEY_CLASSES_ROOT = 0x80000000L;
|
long HKEY_CLASSES_ROOT = 0x80000000L;
|
||||||
long HKEY_CURRENT_USER = 0x80000001L;
|
long HKEY_CURRENT_USER = 0x80000001L;
|
||||||
|
@ -17,11 +17,28 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.util.platform.windows;
|
package org.jackhuang.hmcl.util.platform.windows;
|
||||||
|
|
||||||
|
import com.sun.jna.Memory;
|
||||||
|
import com.sun.jna.Pointer;
|
||||||
|
import com.sun.jna.WString;
|
||||||
|
import com.sun.jna.ptr.IntByReference;
|
||||||
|
import com.sun.jna.ptr.PointerByReference;
|
||||||
|
import org.jackhuang.hmcl.util.platform.NativeUtils;
|
||||||
|
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Glavo
|
* @author Glavo
|
||||||
*/
|
*/
|
||||||
public abstract class WinReg {
|
public abstract class WinReg {
|
||||||
|
|
||||||
|
public static final WinReg INSTANCE = NativeUtils.USE_JNA && Advapi32.INSTANCE != null
|
||||||
|
? new JNAWinReg(Advapi32.INSTANCE) : null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see <a href="https://learn.microsoft.com/windows/win32/sysinfo/predefined-keys">Predefined Keys</a>
|
* @see <a href="https://learn.microsoft.com/windows/win32/sysinfo/predefined-keys">Predefined Keys</a>
|
||||||
*/
|
*/
|
||||||
@ -36,7 +53,6 @@ public abstract class WinReg {
|
|||||||
HKEY_CURRENT_CONFIG(0x80000005),
|
HKEY_CURRENT_CONFIG(0x80000005),
|
||||||
HKEY_DYN_DATA(0x80000006),
|
HKEY_DYN_DATA(0x80000006),
|
||||||
HKEY_CURRENT_USER_LOCAL_SETTINGS(0x80000007);
|
HKEY_CURRENT_USER_LOCAL_SETTINGS(0x80000007);
|
||||||
|
|
||||||
private final int value;
|
private final int value;
|
||||||
|
|
||||||
HKEY(int value) {
|
HKEY(int value) {
|
||||||
@ -46,5 +62,156 @@ public abstract class WinReg {
|
|||||||
public int getValue() {
|
public int getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Pointer toPointer() {
|
||||||
|
return Pointer.createConstant((long) value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean exists(HKEY root, String key);
|
||||||
|
|
||||||
|
public abstract Object queryValue(HKEY root, String key, String valueName);
|
||||||
|
|
||||||
|
public abstract List<String> queryKeys(HKEY root, String key);
|
||||||
|
|
||||||
|
private static final class JNAWinReg extends WinReg {
|
||||||
|
|
||||||
|
private final Advapi32 advapi32;
|
||||||
|
|
||||||
|
JNAWinReg(Advapi32 advapi32) {
|
||||||
|
this.advapi32 = advapi32;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean exists(HKEY root, String key) {
|
||||||
|
PointerByReference phkKey = new PointerByReference();
|
||||||
|
int status = advapi32.RegOpenKeyExW(root.toPointer(), new WString(key), 0, WinConstants.KEY_READ, phkKey);
|
||||||
|
|
||||||
|
if (status == WinConstants.ERROR_SUCCESS) {
|
||||||
|
advapi32.RegCloseKey(phkKey.getValue());
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkLength(int expected, int actual) {
|
||||||
|
if (expected != actual) {
|
||||||
|
throw new IllegalStateException("Expected " + expected + " bytes, but got " + actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object queryValue(HKEY root, String key, String valueName) {
|
||||||
|
PointerByReference phkKey = new PointerByReference();
|
||||||
|
if (advapi32.RegOpenKeyExW(root.toPointer(), new WString(key), 0, WinConstants.KEY_READ, phkKey) != WinConstants.ERROR_SUCCESS)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Pointer hkey = phkKey.getValue();
|
||||||
|
try {
|
||||||
|
IntByReference lpType = new IntByReference();
|
||||||
|
IntByReference lpcbData = new IntByReference();
|
||||||
|
int status = advapi32.RegQueryValueExW(hkey, new WString(valueName), null, lpType, null, lpcbData);
|
||||||
|
if (status != WinConstants.ERROR_SUCCESS) {
|
||||||
|
if (status == WinConstants.ERROR_FILE_NOT_FOUND)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
throw new RuntimeException("Failed to query value: " + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
int type = lpType.getValue();
|
||||||
|
int cbData = lpcbData.getValue();
|
||||||
|
|
||||||
|
try (Memory lpData = new Memory(cbData)) {
|
||||||
|
status = advapi32.RegQueryValueExW(hkey, new WString(valueName), null, null, lpData, lpcbData);
|
||||||
|
if (status != WinConstants.ERROR_SUCCESS) {
|
||||||
|
if (status == WinConstants.ERROR_FILE_NOT_FOUND)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
throw new RuntimeException("Failed to query value: " + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkLength(cbData, lpcbData.getValue());
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case WinConstants.REG_NONE:
|
||||||
|
case WinConstants.REG_BINARY:
|
||||||
|
return lpData.getByteArray(0L, cbData);
|
||||||
|
case WinConstants.REG_DWORD_LITTLE_ENDIAN:
|
||||||
|
case WinConstants.REG_DWORD_BIG_ENDIAN: {
|
||||||
|
checkLength(4, cbData);
|
||||||
|
int value = lpData.getInt(0L);
|
||||||
|
ByteOrder expectedOrder = type == WinConstants.REG_DWORD_LITTLE_ENDIAN ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
|
||||||
|
if (expectedOrder != ByteOrder.nativeOrder())
|
||||||
|
value = Integer.reverseBytes(value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
case WinConstants.REG_QWORD_LITTLE_ENDIAN: {
|
||||||
|
checkLength(8, cbData);
|
||||||
|
long value = lpData.getLong(0L);
|
||||||
|
if (ByteOrder.nativeOrder() != ByteOrder.LITTLE_ENDIAN)
|
||||||
|
value = Long.reverseBytes(value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
case WinConstants.REG_SZ:
|
||||||
|
case WinConstants.REG_EXPAND_SZ:
|
||||||
|
case WinConstants.REG_LINK: {
|
||||||
|
if (cbData < 2)
|
||||||
|
throw new RuntimeException("Illegal length: " + cbData);
|
||||||
|
if (lpData.getChar(cbData - 2) != '\0')
|
||||||
|
throw new RuntimeException("The string does not end with \\0");
|
||||||
|
|
||||||
|
return lpData.getWideString(0L);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unknown reg type: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LOG.warning("Failed to query value", e);
|
||||||
|
} finally {
|
||||||
|
advapi32.RegCloseKey(hkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> queryKeys(HKEY root, String key) {
|
||||||
|
PointerByReference phkKey = new PointerByReference();
|
||||||
|
if (advapi32.RegOpenKeyExW(root.toPointer(), new WString(key), 0, WinConstants.KEY_READ, phkKey) != WinConstants.ERROR_SUCCESS)
|
||||||
|
return Collections.emptyList();
|
||||||
|
|
||||||
|
Pointer hkey = phkKey.getValue();
|
||||||
|
try {
|
||||||
|
String prefix = key.endsWith("\\") ? key : key + "\\";
|
||||||
|
ArrayList<String> res = new ArrayList<>();
|
||||||
|
int maxKeyLength = 256;
|
||||||
|
try (Memory lpName = new Memory(maxKeyLength * 2)) {
|
||||||
|
IntByReference lpcchName = new IntByReference();
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
lpcchName.setValue(maxKeyLength);
|
||||||
|
int status = advapi32.RegEnumKeyExW(hkey, i, lpName, lpcchName, null, null, null, null);
|
||||||
|
if (status == WinConstants.ERROR_SUCCESS) {
|
||||||
|
res.add(prefix + lpName.getWideString(0L));
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
if (status != WinConstants.ERROR_NO_MORE_ITEMS)
|
||||||
|
LOG.warning("Failed to enum key: " + status);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LOG.warning("Failed to query keys", e);
|
||||||
|
} finally {
|
||||||
|
advapi32.RegCloseKey(hkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,4 @@ public interface WinTypes {
|
|||||||
"ullTotalVirtual", "ullAvailVirtual", "ullAvailExtendedVirtual");
|
"ullTotalVirtual", "ullAvailVirtual", "ullAvailExtendedVirtual");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.sun.jna.platform.win32.Advapi32Util;
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
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 static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Glavo
|
||||||
|
*/
|
||||||
|
@EnabledIf("isEnabled")
|
||||||
|
public final class WinRegTest {
|
||||||
|
public static boolean isEnabled() {
|
||||||
|
return WinReg.INSTANCE != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final com.sun.jna.platform.win32.WinReg.HKEY ROOT_KEY = com.sun.jna.platform.win32.WinReg.HKEY_CURRENT_USER;
|
||||||
|
private static final String KEY_BASE = "Software\\JavaSoft\\Prefs\\hmcl\\test";
|
||||||
|
private static String key;
|
||||||
|
|
||||||
|
private static final String[] SUBKEYS = {
|
||||||
|
"Sub0", "Sub1", "Sub2", "Sub3"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final byte[] TEST_DATA = new byte[128];
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (int i = 0; i < TEST_DATA.length; i++) {
|
||||||
|
TEST_DATA[i] = (byte) i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void setup() {
|
||||||
|
key = KEY_BASE + "\\" + UUID.randomUUID();
|
||||||
|
if (!Advapi32Util.registryCreateKey(ROOT_KEY, key))
|
||||||
|
throw new AssertionError("Failed to create key");
|
||||||
|
|
||||||
|
Advapi32Util.registrySetBinaryValue(ROOT_KEY, key, "BINARY", TEST_DATA);
|
||||||
|
Advapi32Util.registrySetStringValue(ROOT_KEY, key, "SZ", "Hello World!");
|
||||||
|
Advapi32Util.registrySetIntValue(ROOT_KEY, key, "DWORD", 0xCAFEBABE);
|
||||||
|
Advapi32Util.registrySetLongValue(ROOT_KEY, key, "QWORD", 0xCAFEBABEL);
|
||||||
|
|
||||||
|
for (String subkey : SUBKEYS) {
|
||||||
|
if (!Advapi32Util.registryCreateKey(ROOT_KEY, key, subkey))
|
||||||
|
throw new AssertionError("Failed to create key");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
public static void cleanUp() {
|
||||||
|
if (key != null) {
|
||||||
|
if (!Advapi32Util.registryKeyExists(ROOT_KEY, key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (String subKey : Advapi32Util.registryGetKeys(ROOT_KEY, key))
|
||||||
|
Advapi32Util.registryDeleteKey(ROOT_KEY, key, subKey);
|
||||||
|
|
||||||
|
Advapi32Util.registryDeleteKey(ROOT_KEY, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQueryValue() {
|
||||||
|
WinReg.HKEY hkey = WinReg.HKEY.HKEY_CURRENT_USER;
|
||||||
|
WinReg reg = WinReg.INSTANCE;
|
||||||
|
|
||||||
|
assertArrayEquals(TEST_DATA, (byte[]) reg.queryValue(hkey, key, "BINARY"));
|
||||||
|
assertEquals("Hello World!", reg.queryValue(hkey, key, "SZ"));
|
||||||
|
assertEquals(0xCAFEBABE, reg.queryValue(hkey, key, "DWORD"));
|
||||||
|
assertEquals(0xCAFEBABEL, reg.queryValue(hkey, key, "QWORD"));
|
||||||
|
assertNull(reg.queryValue(hkey, key, "UNKNOWN"));
|
||||||
|
assertNull(reg.queryValue(hkey, KEY_BASE + "\\" + "NOT_EXIST", "UNKNOWN"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQueryKeys() {
|
||||||
|
WinReg.HKEY hkey = WinReg.HKEY.HKEY_CURRENT_USER;
|
||||||
|
WinReg reg = WinReg.INSTANCE;
|
||||||
|
|
||||||
|
assertEquals(Arrays.asList(SUBKEYS).stream().map(it -> key + "\\" + it).collect(Collectors.toList()),
|
||||||
|
reg.queryKeys(hkey, key).stream().sorted().collect(Collectors.toList()));
|
||||||
|
for (String subkey : SUBKEYS) {
|
||||||
|
assertEquals(Collections.emptyList(), reg.queryKeys(hkey, key + "\\" + subkey));
|
||||||
|
}
|
||||||
|
assertEquals(Collections.emptyList(), reg.queryKeys(hkey, key + "\\NOT_EXIST"));
|
||||||
|
}
|
||||||
|
}
|
@ -34,6 +34,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
|
|||||||
chardet = { module = "org.glavo:chardet", version.ref = "chardet" }
|
chardet = { module = "org.glavo:chardet", version.ref = "chardet" }
|
||||||
twelvemonkeys-imageio-webp = { module = "com.twelvemonkeys.imageio:imageio-webp", version.ref = "twelvemonkeys" }
|
twelvemonkeys-imageio-webp = { module = "com.twelvemonkeys.imageio:imageio-webp", version.ref = "twelvemonkeys" }
|
||||||
jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }
|
jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }
|
||||||
|
jna-platform = { module = "net.java.dev.jna:jna-platform", version.ref = "jna" }
|
||||||
pci-ids = { module = "org.glavo:pci-ids", version.ref = "pci-ids" }
|
pci-ids = { module = "org.glavo:pci-ids", version.ref = "pci-ids" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user