Java is a cross platform language and therefore does not support platform specific features. Windows has a special unique feature - registry. So, java cannot access registry. We have to use JNI or external processes (directly or indirectly) to do this.
Solution
But recently I found a cool Java feature: preferences implemented by class
java.util.prefs.Preferences
. Yes, I am eating my hat now! This feature was introduced to Java 1.4 and I did not know and have never used it! What's cool in this feature? Its implementation is platform specific. As always we can access abstract class java.util.prefs.Preferences
and get its instances using several static method like userRoot()
, systemRoot()
and others. But the concrete implementation depends on current operating system. Class WindowsPreferences
uses registry to store values. It declares several native methods: WindowsRegOpenKey, WindowsRegCloseKey, WindowsRegDeleteKey
etc. Sounds good, doesn't it?So I decided to abuse this class and implement access to registry utilizing these methods. It was not very simple task. The class
WindowsPreferences
uses only specific registry path: Software\Java Soft\Prefs
under HKLM
and HKCU
. Class itself is package protected and cannot be nether inherited nor called. So, reflection is the only real option. Implementation
I called my class
Registry
and decided to make it singleton. It contains static enum Hive that enumerates all standard registry hives (HKLM, HKCU, HKCR etc). It implements the following public methods:keys(Hive hive, String path)
values(Hive hive, String path)
get(Hive hive, String path, String name)
createKey(Hive hive, String path)
put(Hive hive, String path, String name, String value)
removeKey(Hive hive, String path)
removeValue(Hive hive, String path, String name)
The implementation is based on invocation of private native methods declared in
WindowsPreferences
class using reflection:
private <R> R call(String methodName, Class[] types, Object[] args) {
try {
Method m = winPrefClazz.getDeclaredMethod(methodName, types);
m.setAccessible(true);
return (R)m.invoke(null, args);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Here is the example how this method is used. This is what I would like to write:
int hresult = WindowsRegCloseKey(handle);
This is what I have to do instead:
int result = call("WindowsRegCloseKey", new Class[] {int.class}, new Object[] {handle});
Using reflection sometimes has some benefits. For example methods that enumerate registry keys and values are very similar but they call different native methods (e.g.
WindowsRegEnumValue
and WindowsRegEnumKeyEx
). So methods childrenNamesSpi() and keysSpi() are almost duplicate in WindowsPreferences.
Using reflection allows creating one implementation for both cases.Limitations
Careful examining of the API exposed by registry class shows that it is limited to work with string values only: method put accepts value of string type, method get returns string. Unfortunately this is the limitation of current approach. Class
WindowsPreferences
is not designed to work with other value types. Native method WindowsRegSetValueEx
works with string values only; attempt to retrieve value of other type using WindowsRegQueryValueEx
returns null.Conclusions
Java programmers are regular to use native libraries to utilize platform specific features. But sometimes some of the features are already implemented by JVM but just not exposed as a public API. Investigation of JDK classes and utilizing of reflection allows us to get access to windows registry with simple and very compact java code. The implementation deals with string values only that is a serious limitation but still can be used because most values stored in registry are strings. The suggested technique is most useful when code size is critical. For example for applications started using JNLP.
Full source code of Registry class and unit test may be found here.
References
I found this trick myself and wrote this article. Then I googled "read windows registry with java" and found at least 2 similar implementations:
http://lenkite.blogspot.com/2008/05/access-windows-registry-using-java.html
http://www.davidc.net/programming/java/reading-windows-registry-java-without-jni
I have no idea why I did not search web before writing the code... The advantage of my solution is that I implemented full API that can be used in any application while these guys just showed that this solution is possible.
hi..
ReplyDeletegood day!:)
i wanna ask.. does using WindowsPreferences limits you to write only at the specific path: Software\Java Soft\Prefs ?
Hope to receive an answer from you asap.
Thank you.
You are right. WindowsPreferences automatically access only special registry path under Java Soft. But this is exactly the point of my article: I hacked the internal API and used it to access registry at other locations.
ReplyDelete