source: josm/trunk/src/org/openstreetmap/josm/tools/WinRegistry.java@ 12218

Last change on this file since 12218 was 12217, checked in by Don-vip, 7 years ago

see #14821 - workaround for JDK-8180379/JDK-8179014 : prevent JVM crash when opening a file chooser on Windows 10 Creators Update with Windows look & feel + add information about OS build number for Windows & macOS + add utilities to get Java update/build version numbers

  • Property svn:eol-style set to native
File size: 10.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import java.lang.reflect.InvocationTargetException;
5import java.lang.reflect.Method;
6import java.nio.charset.StandardCharsets;
7import java.util.ArrayList;
8import java.util.Arrays;
9import java.util.Collections;
10import java.util.HashMap;
11import java.util.List;
12import java.util.Map;
13import 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 */
23public 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}
Note: See TracBrowser for help on using the repository browser.