[12217] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
| 2 | package org.openstreetmap.josm.tools;
|
---|
| 3 |
|
---|
| 4 | import java.lang.reflect.InvocationTargetException;
|
---|
| 5 | import java.lang.reflect.Method;
|
---|
| 6 | import java.nio.charset.StandardCharsets;
|
---|
| 7 | import java.util.ArrayList;
|
---|
| 8 | import java.util.Arrays;
|
---|
| 9 | import java.util.Collections;
|
---|
| 10 | import java.util.HashMap;
|
---|
| 11 | import java.util.List;
|
---|
| 12 | import java.util.Map;
|
---|
| 13 | import java.util.prefs.Preferences;
|
---|
| 14 |
|
---|
| 15 | /**
|
---|
| 16 | * Utility class to access Window registry (read access only).
|
---|
| 17 | * As the implementation relies on internal JDK class {@code java.util.prefs.WindowsPreferences} and its native JNI
|
---|
| 18 | * method {@code Java_java_util_prefs_WindowsPreferences_WindowsRegQueryValueEx}, only String values (REG_SZ)
|
---|
| 19 | * are supported.
|
---|
| 20 | * Adapted from <a href="http://stackoverflow.com/a/6163701/2257172">StackOverflow</a>.
|
---|
| 21 | * @since 12217
|
---|
| 22 | */
|
---|
| 23 | public final class WinRegistry {
|
---|
| 24 |
|
---|
| 25 | /**
|
---|
| 26 | * Registry entries subordinate to this key define the preferences of the current user.
|
---|
| 27 | * These preferences include the settings of environment variables, data about program groups,
|
---|
| 28 | * colors, printers, network connections, and application preferences.
|
---|
| 29 | * See <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms724836(v=vs.85).aspx">Predefined Keys</a>
|
---|
| 30 | */
|
---|
| 31 | public static final int HKEY_CURRENT_USER = 0x80000001;
|
---|
| 32 |
|
---|
| 33 | /**
|
---|
| 34 | * Registry entries subordinate to this key define the physical state of the computer, including data about the bus type,
|
---|
| 35 | * system memory, and installed hardware and software. It contains subkeys that hold current configuration data,
|
---|
| 36 | * including Plug and Play information (the Enum branch, which includes a complete list of all hardware that has ever been
|
---|
| 37 | * on the system), network logon preferences, network security information, software-related information (such as server
|
---|
| 38 | * names and the location of the server), and other system information.
|
---|
| 39 | * See <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms724836(v=vs.85).aspx">Predefined Keys</a>
|
---|
| 40 | */
|
---|
| 41 | public static final int HKEY_LOCAL_MACHINE = 0x80000002;
|
---|
| 42 |
|
---|
[13651] | 43 | private static final long REG_SUCCESS = 0L;
|
---|
[12217] | 44 |
|
---|
| 45 | private static final int KEY_READ = 0x20019;
|
---|
| 46 | private static final Preferences userRoot = Preferences.userRoot();
|
---|
| 47 | private static final Preferences systemRoot = Preferences.systemRoot();
|
---|
| 48 | private static final Class<? extends Preferences> userClass = userRoot.getClass();
|
---|
| 49 | private static final Method regOpenKey;
|
---|
| 50 | private static final Method regCloseKey;
|
---|
| 51 | private static final Method regQueryValueEx;
|
---|
| 52 | private static final Method regEnumValue;
|
---|
| 53 | private static final Method regQueryInfoKey;
|
---|
| 54 | private static final Method regEnumKeyEx;
|
---|
| 55 |
|
---|
[13651] | 56 | private static boolean java11;
|
---|
| 57 |
|
---|
[12217] | 58 | static {
|
---|
[13651] | 59 | regOpenKey = getDeclaredMethod("WindowsRegOpenKey", int.class, byte[].class, int.class);
|
---|
| 60 | regCloseKey = getDeclaredMethod("WindowsRegCloseKey", int.class);
|
---|
| 61 | regQueryValueEx = getDeclaredMethod("WindowsRegQueryValueEx", int.class, byte[].class);
|
---|
| 62 | regEnumValue = getDeclaredMethod("WindowsRegEnumValue", int.class, int.class, int.class);
|
---|
| 63 | regQueryInfoKey = getDeclaredMethod("WindowsRegQueryInfoKey1", int.class);
|
---|
| 64 | regEnumKeyEx = getDeclaredMethod("WindowsRegEnumKeyEx", int.class, int.class, int.class);
|
---|
| 65 | Utils.setObjectsAccessible(regOpenKey, regCloseKey, regQueryValueEx, regEnumValue, regQueryInfoKey, regEnumKeyEx);
|
---|
| 66 | }
|
---|
| 67 |
|
---|
| 68 | private static Method getDeclaredMethod(String name, Class<?>... parameterTypes) {
|
---|
[12217] | 69 | try {
|
---|
[13651] | 70 | return userClass.getDeclaredMethod(name, parameterTypes);
|
---|
| 71 | } catch (NoSuchMethodException e) {
|
---|
| 72 | if (parameterTypes.length > 0 && parameterTypes[0] == int.class) {
|
---|
| 73 | // JDK-8198899: change of signature in Java 11. Old signature to drop when we switch to Java 11
|
---|
| 74 | Class<?>[] parameterTypesCopy = Utils.copyArray(parameterTypes);
|
---|
| 75 | parameterTypesCopy[0] = long.class;
|
---|
| 76 | java11 = true;
|
---|
| 77 | return getDeclaredMethod(name, parameterTypesCopy);
|
---|
| 78 | }
|
---|
| 79 | Logging.log(Logging.LEVEL_ERROR, "Unable to find WindowsReg method", e);
|
---|
| 80 | return null;
|
---|
| 81 | } catch (RuntimeException e) {
|
---|
| 82 | Logging.log(Logging.LEVEL_ERROR, "Unable to get WindowsReg method", e);
|
---|
| 83 | return null;
|
---|
[12217] | 84 | }
|
---|
| 85 | }
|
---|
| 86 |
|
---|
[13651] | 87 | private static Number hkey(int key) {
|
---|
| 88 | return java11 ? ((Number) Long.valueOf(key)) : ((Number) Integer.valueOf(key));
|
---|
| 89 | }
|
---|
| 90 |
|
---|
[12217] | 91 | private WinRegistry() {
|
---|
| 92 | // Hide default constructor for utilities classes
|
---|
| 93 | }
|
---|
| 94 |
|
---|
| 95 | /**
|
---|
| 96 | * Read a value from key and value name
|
---|
| 97 | * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
|
---|
| 98 | * @param key key name
|
---|
| 99 | * @param valueName value name
|
---|
| 100 | * @return the value
|
---|
| 101 | * @throws IllegalArgumentException if hkey not HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
|
---|
| 102 | * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
|
---|
| 103 | * @throws InvocationTargetException if the underlying method throws an exception
|
---|
| 104 | */
|
---|
| 105 | public static String readString(int hkey, String key, String valueName)
|
---|
| 106 | throws IllegalAccessException, InvocationTargetException {
|
---|
| 107 | if (hkey == HKEY_LOCAL_MACHINE) {
|
---|
| 108 | return readString(systemRoot, hkey, key, valueName);
|
---|
| 109 | } else if (hkey == HKEY_CURRENT_USER) {
|
---|
| 110 | return readString(userRoot, hkey, key, valueName);
|
---|
| 111 | } else {
|
---|
| 112 | throw new IllegalArgumentException("hkey=" + hkey);
|
---|
| 113 | }
|
---|
| 114 | }
|
---|
| 115 |
|
---|
| 116 | /**
|
---|
| 117 | * Read value(s) and value name(s) form given key
|
---|
| 118 | * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
|
---|
| 119 | * @param key key name
|
---|
| 120 | * @return the value name(s) plus the value(s)
|
---|
| 121 | * @throws IllegalArgumentException if hkey not HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
|
---|
| 122 | * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
|
---|
| 123 | * @throws InvocationTargetException if the underlying method throws an exception
|
---|
| 124 | */
|
---|
| 125 | public static Map<String, String> readStringValues(int hkey, String key)
|
---|
| 126 | throws IllegalAccessException, InvocationTargetException {
|
---|
| 127 | if (hkey == HKEY_LOCAL_MACHINE) {
|
---|
| 128 | return readStringValues(systemRoot, hkey, key);
|
---|
| 129 | } else if (hkey == HKEY_CURRENT_USER) {
|
---|
| 130 | return readStringValues(userRoot, hkey, key);
|
---|
| 131 | } else {
|
---|
| 132 | throw new IllegalArgumentException("hkey=" + hkey);
|
---|
| 133 | }
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | /**
|
---|
| 137 | * Read the value name(s) from a given key
|
---|
| 138 | * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
|
---|
| 139 | * @param key key name
|
---|
| 140 | * @return the value name(s)
|
---|
| 141 | * @throws IllegalArgumentException if hkey not HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
|
---|
| 142 | * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
|
---|
| 143 | * @throws InvocationTargetException if the underlying method throws an exception
|
---|
| 144 | */
|
---|
| 145 | public static List<String> readStringSubKeys(int hkey, String key)
|
---|
| 146 | throws IllegalAccessException, InvocationTargetException {
|
---|
| 147 | if (hkey == HKEY_LOCAL_MACHINE) {
|
---|
| 148 | return readStringSubKeys(systemRoot, hkey, key);
|
---|
| 149 | } else if (hkey == HKEY_CURRENT_USER) {
|
---|
| 150 | return readStringSubKeys(userRoot, hkey, key);
|
---|
| 151 | } else {
|
---|
| 152 | throw new IllegalArgumentException("hkey=" + hkey);
|
---|
| 153 | }
|
---|
| 154 | }
|
---|
| 155 |
|
---|
| 156 | // =====================
|
---|
| 157 |
|
---|
[13651] | 158 | private static Number getNumber(Object array, int index) {
|
---|
| 159 | if (array instanceof int[]) {
|
---|
| 160 | return ((int[]) array)[index];
|
---|
| 161 | } else if (array instanceof long[]) {
|
---|
| 162 | return ((long[]) array)[index];
|
---|
| 163 | }
|
---|
| 164 | throw new IllegalArgumentException();
|
---|
| 165 | }
|
---|
| 166 |
|
---|
[12217] | 167 | private static String readString(Preferences root, int hkey, String key, String value)
|
---|
| 168 | throws IllegalAccessException, InvocationTargetException {
|
---|
[13651] | 169 | if (regOpenKey == null || regQueryValueEx == null || regCloseKey == null) {
|
---|
[12217] | 170 | return null;
|
---|
| 171 | }
|
---|
[13651] | 172 | // Need to capture both int[] (Java 8-10) and long[] (Java 11+)
|
---|
| 173 | Object handles = regOpenKey.invoke(root, hkey(hkey), toCstr(key), Integer.valueOf(KEY_READ));
|
---|
| 174 | if (getNumber(handles, 1).longValue() != REG_SUCCESS) {
|
---|
| 175 | return null;
|
---|
| 176 | }
|
---|
| 177 | byte[] valb = (byte[]) regQueryValueEx.invoke(root, getNumber(handles, 0), toCstr(value));
|
---|
| 178 | regCloseKey.invoke(root, getNumber(handles, 0));
|
---|
[12217] | 179 | return (valb != null ? new String(valb, StandardCharsets.UTF_8).trim() : null);
|
---|
| 180 | }
|
---|
| 181 |
|
---|
| 182 | private static Map<String, String> readStringValues(Preferences root, int hkey, String key)
|
---|
| 183 | throws IllegalAccessException, InvocationTargetException {
|
---|
[13651] | 184 | if (regOpenKey == null || regQueryInfoKey == null || regEnumValue == null || regCloseKey == null) {
|
---|
[13672] | 185 | return Collections.emptyMap();
|
---|
[13651] | 186 | }
|
---|
[12217] | 187 | HashMap<String, String> results = new HashMap<>();
|
---|
[13651] | 188 | // Need to capture both int[] (Java 8-10) and long[] (Java 11+)
|
---|
| 189 | Object handles = regOpenKey.invoke(root, hkey(hkey), toCstr(key), Integer.valueOf(KEY_READ));
|
---|
| 190 | if (getNumber(handles, 1).longValue() != REG_SUCCESS) {
|
---|
[13672] | 191 | return Collections.emptyMap();
|
---|
[12217] | 192 | }
|
---|
[13651] | 193 | // Need to capture both int[] (Java 8-10) and long[] (Java 11+)
|
---|
| 194 | Object info = regQueryInfoKey.invoke(root, getNumber(handles, 0));
|
---|
[12217] | 195 |
|
---|
[13651] | 196 | int count = getNumber(info, 0).intValue();
|
---|
| 197 | int maxlen = getNumber(info, 3).intValue();
|
---|
[12217] | 198 | for (int index = 0; index < count; index++) {
|
---|
[13651] | 199 | byte[] name = (byte[]) regEnumValue.invoke(root, getNumber(handles, 0), Integer.valueOf(index), Integer.valueOf(maxlen + 1));
|
---|
[12217] | 200 | String value = readString(hkey, key, new String(name, StandardCharsets.UTF_8));
|
---|
| 201 | results.put(new String(name, StandardCharsets.UTF_8).trim(), value);
|
---|
| 202 | }
|
---|
[13651] | 203 | regCloseKey.invoke(root, getNumber(handles, 0));
|
---|
[12217] | 204 | return results;
|
---|
| 205 | }
|
---|
| 206 |
|
---|
| 207 | private static List<String> readStringSubKeys(Preferences root, int hkey, String key)
|
---|
| 208 | throws IllegalAccessException, InvocationTargetException {
|
---|
[13651] | 209 | if (regOpenKey == null || regQueryInfoKey == null || regEnumKeyEx == null || regCloseKey == null) {
|
---|
[13672] | 210 | return Collections.emptyList();
|
---|
[13651] | 211 | }
|
---|
[12217] | 212 | List<String> results = new ArrayList<>();
|
---|
[13651] | 213 | // Need to capture both int[] (Java 8-10) and long[] (Java 11+)
|
---|
| 214 | Object handles = regOpenKey.invoke(root, hkey(hkey), toCstr(key), Integer.valueOf(KEY_READ));
|
---|
| 215 | if (getNumber(handles, 1).longValue() != REG_SUCCESS) {
|
---|
[12217] | 216 | return Collections.emptyList();
|
---|
| 217 | }
|
---|
[13651] | 218 | // Need to capture both int[] (Java 8-10) and long[] (Java 11+)
|
---|
| 219 | Object info = regQueryInfoKey.invoke(root, getNumber(handles, 0));
|
---|
[12217] | 220 |
|
---|
[13651] | 221 | int count = getNumber(info, 0).intValue();
|
---|
| 222 | int maxlen = getNumber(info, 3).intValue();
|
---|
[12217] | 223 | for (int index = 0; index < count; index++) {
|
---|
[13651] | 224 | byte[] name = (byte[]) regEnumKeyEx.invoke(root, getNumber(handles, 0), Integer.valueOf(index), Integer.valueOf(maxlen + 1));
|
---|
[12217] | 225 | results.add(new String(name, StandardCharsets.UTF_8).trim());
|
---|
| 226 | }
|
---|
[13651] | 227 | regCloseKey.invoke(root, getNumber(handles, 0));
|
---|
[12217] | 228 | return results;
|
---|
| 229 | }
|
---|
| 230 |
|
---|
| 231 | // utility
|
---|
| 232 | private static byte[] toCstr(String str) {
|
---|
| 233 | byte[] array = str.getBytes(StandardCharsets.UTF_8);
|
---|
| 234 | byte[] biggerCopy = Arrays.copyOf(array, array.length + 1);
|
---|
| 235 | biggerCopy[array.length] = 0;
|
---|
| 236 | return biggerCopy;
|
---|
| 237 | }
|
---|
| 238 | }
|
---|