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 |
|
---|
43 | private static final int REG_SUCCESS = 0;
|
---|
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 |
|
---|
56 | static {
|
---|
57 | try {
|
---|
58 | regOpenKey = userClass.getDeclaredMethod("WindowsRegOpenKey",
|
---|
59 | new Class[] { int.class, byte[].class, int.class });
|
---|
60 | regOpenKey.setAccessible(true);
|
---|
61 | regCloseKey = userClass.getDeclaredMethod("WindowsRegCloseKey",
|
---|
62 | new Class[] { int.class });
|
---|
63 | regCloseKey.setAccessible(true);
|
---|
64 | regQueryValueEx = userClass.getDeclaredMethod("WindowsRegQueryValueEx",
|
---|
65 | new Class[] { int.class, byte[].class });
|
---|
66 | regQueryValueEx.setAccessible(true);
|
---|
67 | regEnumValue = userClass.getDeclaredMethod("WindowsRegEnumValue",
|
---|
68 | new Class[] { int.class, int.class, int.class });
|
---|
69 | regEnumValue.setAccessible(true);
|
---|
70 | regQueryInfoKey = userClass.getDeclaredMethod("WindowsRegQueryInfoKey1",
|
---|
71 | new Class[] { int.class });
|
---|
72 | regQueryInfoKey.setAccessible(true);
|
---|
73 | regEnumKeyEx = userClass.getDeclaredMethod("WindowsRegEnumKeyEx",
|
---|
74 | new Class[] { int.class, int.class, int.class });
|
---|
75 | regEnumKeyEx.setAccessible(true);
|
---|
76 | } catch (SecurityException | ReflectiveOperationException e) {
|
---|
77 | throw new JosmRuntimeException(e);
|
---|
78 | }
|
---|
79 | }
|
---|
80 |
|
---|
81 | private WinRegistry() {
|
---|
82 | // Hide default constructor for utilities classes
|
---|
83 | }
|
---|
84 |
|
---|
85 | /**
|
---|
86 | * Read a value from key and value name
|
---|
87 | * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
|
---|
88 | * @param key key name
|
---|
89 | * @param valueName value name
|
---|
90 | * @return the value
|
---|
91 | * @throws IllegalArgumentException if hkey not HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
|
---|
92 | * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
|
---|
93 | * @throws InvocationTargetException if the underlying method throws an exception
|
---|
94 | */
|
---|
95 | public static String readString(int hkey, String key, String valueName)
|
---|
96 | throws IllegalAccessException, InvocationTargetException {
|
---|
97 | if (hkey == HKEY_LOCAL_MACHINE) {
|
---|
98 | return readString(systemRoot, hkey, key, valueName);
|
---|
99 | } else if (hkey == HKEY_CURRENT_USER) {
|
---|
100 | return readString(userRoot, hkey, key, valueName);
|
---|
101 | } else {
|
---|
102 | throw new IllegalArgumentException("hkey=" + hkey);
|
---|
103 | }
|
---|
104 | }
|
---|
105 |
|
---|
106 | /**
|
---|
107 | * Read value(s) and value name(s) form given key
|
---|
108 | * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
|
---|
109 | * @param key key name
|
---|
110 | * @return the value name(s) plus the value(s)
|
---|
111 | * @throws IllegalArgumentException if hkey not HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
|
---|
112 | * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
|
---|
113 | * @throws InvocationTargetException if the underlying method throws an exception
|
---|
114 | */
|
---|
115 | public static Map<String, String> readStringValues(int hkey, String key)
|
---|
116 | throws IllegalAccessException, InvocationTargetException {
|
---|
117 | if (hkey == HKEY_LOCAL_MACHINE) {
|
---|
118 | return readStringValues(systemRoot, hkey, key);
|
---|
119 | } else if (hkey == HKEY_CURRENT_USER) {
|
---|
120 | return readStringValues(userRoot, hkey, key);
|
---|
121 | } else {
|
---|
122 | throw new IllegalArgumentException("hkey=" + hkey);
|
---|
123 | }
|
---|
124 | }
|
---|
125 |
|
---|
126 | /**
|
---|
127 | * Read the value name(s) from a given key
|
---|
128 | * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
|
---|
129 | * @param key key name
|
---|
130 | * @return the value name(s)
|
---|
131 | * @throws IllegalArgumentException if hkey not HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
|
---|
132 | * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
|
---|
133 | * @throws InvocationTargetException if the underlying method throws an exception
|
---|
134 | */
|
---|
135 | public static List<String> readStringSubKeys(int hkey, String key)
|
---|
136 | throws IllegalAccessException, InvocationTargetException {
|
---|
137 | if (hkey == HKEY_LOCAL_MACHINE) {
|
---|
138 | return readStringSubKeys(systemRoot, hkey, key);
|
---|
139 | } else if (hkey == HKEY_CURRENT_USER) {
|
---|
140 | return readStringSubKeys(userRoot, hkey, key);
|
---|
141 | } else {
|
---|
142 | throw new IllegalArgumentException("hkey=" + hkey);
|
---|
143 | }
|
---|
144 | }
|
---|
145 |
|
---|
146 | // =====================
|
---|
147 |
|
---|
148 | private static String readString(Preferences root, int hkey, String key, String value)
|
---|
149 | throws IllegalAccessException, InvocationTargetException {
|
---|
150 | int[] handles = (int[]) regOpenKey.invoke(root,
|
---|
151 | new Object[] { Integer.valueOf(hkey), toCstr(key), Integer.valueOf(KEY_READ) });
|
---|
152 | if (handles[1] != REG_SUCCESS) {
|
---|
153 | return null;
|
---|
154 | }
|
---|
155 | byte[] valb = (byte[]) regQueryValueEx.invoke(root, new Object[] { Integer.valueOf(handles[0]), toCstr(value) });
|
---|
156 | regCloseKey.invoke(root, new Object[] { Integer.valueOf(handles[0]) });
|
---|
157 | return (valb != null ? new String(valb, StandardCharsets.UTF_8).trim() : null);
|
---|
158 | }
|
---|
159 |
|
---|
160 | private static Map<String, String> readStringValues(Preferences root, int hkey, String key)
|
---|
161 | throws IllegalAccessException, InvocationTargetException {
|
---|
162 | HashMap<String, String> results = new HashMap<>();
|
---|
163 | int[] handles = (int[]) regOpenKey.invoke(root,
|
---|
164 | new Object[] { Integer.valueOf(hkey), toCstr(key), Integer.valueOf(KEY_READ) });
|
---|
165 | if (handles[1] != REG_SUCCESS) {
|
---|
166 | return null;
|
---|
167 | }
|
---|
168 | int[] info = (int[]) regQueryInfoKey.invoke(root, new Object[] { Integer.valueOf(handles[0]) });
|
---|
169 |
|
---|
170 | int count = info[0]; // count
|
---|
171 | int maxlen = info[3]; // value length max
|
---|
172 | for (int index = 0; index < count; index++) {
|
---|
173 | byte[] name = (byte[]) regEnumValue.invoke(root,
|
---|
174 | new Object[] { Integer.valueOf(handles[0]), Integer.valueOf(index), Integer.valueOf(maxlen + 1) });
|
---|
175 | String value = readString(hkey, key, new String(name, StandardCharsets.UTF_8));
|
---|
176 | results.put(new String(name, StandardCharsets.UTF_8).trim(), value);
|
---|
177 | }
|
---|
178 | regCloseKey.invoke(root, new Object[] { Integer.valueOf(handles[0]) });
|
---|
179 | return results;
|
---|
180 | }
|
---|
181 |
|
---|
182 | private static List<String> readStringSubKeys(Preferences root, int hkey, String key)
|
---|
183 | throws IllegalAccessException, InvocationTargetException {
|
---|
184 | List<String> results = new ArrayList<>();
|
---|
185 | int[] handles = (int[]) regOpenKey.invoke(root,
|
---|
186 | new Object[] { Integer.valueOf(hkey), toCstr(key), Integer.valueOf(KEY_READ) });
|
---|
187 | if (handles[1] != REG_SUCCESS) {
|
---|
188 | return Collections.emptyList();
|
---|
189 | }
|
---|
190 | int[] info = (int[]) regQueryInfoKey.invoke(root, new Object[] { Integer.valueOf(handles[0]) });
|
---|
191 |
|
---|
192 | int count = info[0]; // Fix: info[2] was being used here with wrong results. Suggested by davenpcj, confirmed by Petrucio
|
---|
193 | int maxlen = info[3]; // value length max
|
---|
194 | for (int index = 0; index < count; index++) {
|
---|
195 | byte[] name = (byte[]) regEnumKeyEx.invoke(root,
|
---|
196 | new Object[] { Integer.valueOf(handles[0]), Integer.valueOf(index), Integer.valueOf(maxlen + 1) });
|
---|
197 | results.add(new String(name, StandardCharsets.UTF_8).trim());
|
---|
198 | }
|
---|
199 | regCloseKey.invoke(root, new Object[] { Integer.valueOf(handles[0]) });
|
---|
200 | return results;
|
---|
201 | }
|
---|
202 |
|
---|
203 | // utility
|
---|
204 | private static byte[] toCstr(String str) {
|
---|
205 | byte[] array = str.getBytes(StandardCharsets.UTF_8);
|
---|
206 | byte[] biggerCopy = Arrays.copyOf(array, array.length + 1);
|
---|
207 | biggerCopy[array.length] = 0;
|
---|
208 | return biggerCopy;
|
---|
209 | }
|
---|
210 | }
|
---|