业务介绍
最近项目需要通过Java程序操作Windows注册表(本次测试中使用的是Win64位环境),因为上次使用JNI都是好久前的事情了,重新翻开书(Java核心技术卷2,我这是第8版)又看了一遍,把新遇到的问题记录下.
原书例子
书里的例子Win32Reg的包里面有
- Win32RegKey.java :操作注册表的主要方法类,里面主要有三个类(Win32RegKey;Win32RegKeyException;Win32RegKeyNameEnumeration),这部分实际生产中可以根据自己需要修改
- Win32RegKey.c :JNI部分需要和Windows通信的代码,这里如果修改了包结构,相关的C代码或逻辑需要自己修改
- Win32RegKeyTest.java :测试注册表写入的类
生成头文件
按照JNI调用的基本方式,需要将Win32RegKey.c编译成动态dll文件,然后将dll文件放到jdk>jre的环境中,或者注册到windows的环境中.首先将编译Win32RegKey.java
javac Win32RegKey.java
编译后生成对应的3个class文件Win32RegKey.class\Win32RegKeyException.class\Win32RegKeyNameEnumeration.class.
在Win32RegKey.c中需要两个头文件,书中原例子代码如下,可根据实际情况修改
/**
@version 1.00 1997-07-01
@author Cay Horstmann
*/
#include "Win32RegKey.h"
#include "Win32RegKeyNameEnumeration.h"
#include <string.h>
#include <stdlib.h>
#include <windows.h>
JNIEXPORT jobject JNICALL Java_Win32RegKey_getValue(JNIEnv* env, jobject this_obj, jobject name)
{
const char* cname;
jstring path;
const char* cpath;
HKEY hkey;
DWORD type;
DWORD size;
jclass this_class;
jfieldID id_root;
jfieldID id_path;
HKEY root;
jobject ret;
char* cret;
/* get the class */
this_class = (*env)->GetObjectClass(env, this_obj);
/* get the field IDs */
id_root = (*env)->GetFieldID(env, this_class, "root", "I");
id_path = (*env)->GetFieldID(env, this_class, "path", "Ljava/lang/String;");
/* get the fields */
root = (HKEY) (*env)->GetIntField(env, this_obj, id_root);
path = (jstring)(*env)->GetObjectField(env, this_obj, id_path);
cpath = (*env)->GetStringUTFChars(env, path, NULL);
/* open the registry key */
if (RegOpenKeyEx(root, cpath, 0, KEY_READ, &hkey) != ERROR_SUCCESS)
{
(*env)->ThrowNew(env, (*env)->FindClass(env, "Win32RegKeyException"),
"Open key failed");
(*env)->ReleaseStringUTFChars(env, path, cpath);
return NULL;
}
(*env)->ReleaseStringUTFChars(env, path, cpath);
cname = (*env)->GetStringUTFChars(env, name, NULL);
/* find the type and size of the value */
if (RegQueryValueEx(hkey, cname, NULL, &type, NULL, &size) != ERROR_SUCCESS)
{
(*env)->ThrowNew(env, (*env)->FindClass(env, "Win32RegKeyException"),
"Query value key failed");
RegCloseKey(hkey);
(*env)->ReleaseStringUTFChars(env, name, cname);
return NULL;
}
/* get memory to hold the value */
cret = (char*)malloc(size);
/* read the value */
if (RegQueryValueEx(hkey, cname, NULL, &type, cret, &size) != ERROR_SUCCESS)
{
(*env)->ThrowNew(env, (*env)->FindClass(env, "Win32RegKeyException"),
"Query value key failed");
free(cret);
RegCloseKey(hkey);
(*env)->ReleaseStringUTFChars(env, name, cname);
return NULL;
}
/* depending on the type, store the value in a string,
integer or byte array */
if (type == REG_SZ)
{
ret = (*env)->NewStringUTF(env, cret);
}
else if (type == REG_DWORD)
{
jclass class_Integer = (*env)->FindClass(env, "java/lang/Integer");
/* get the method ID of the constructor */
jmethodID id_Integer = (*env)->GetMethodID(env, class_Integer, "<init>", "(I)V");
int value = *(int*) cret;
/* invoke the constructor */
ret = (*env)->NewObject(env, class_Integer, id_Integer, value);
}
else if (type == REG_BINARY)
{
ret = (*env)->NewByteArray(env, size);
(*env)->SetByteArrayRegion(env, (jarray) ret, 0, size, cret);
}
else
{
(*env)->ThrowNew(env, (*env)->FindClass(env, "Win32RegKeyException"),
"Unsupported value type");
ret = NULL;
}
free(cret);
RegCloseKey(hkey);
(*env)->ReleaseStringUTFChars(env, name, cname);
return ret;
}
JNIEXPORT void JNICALL Java_Win32RegKey_setValue(JNIEnv* env, jobject this_obj,
jstring name, jobject value)
{
const char* cname;
jstring path;
const char* cpath;
HKEY hkey;
DWORD type;
DWORD size;
jclass this_class;
jclass class_value;
jclass class_Integer;
jfieldID id_root;
jfieldID id_path;
HKEY root;
const char* cvalue;
int ivalue;
/* get the class */
this_class = (*env)->GetObjectClass(env, this_obj);
/* get the field IDs */
id_root = (*env)->GetFieldID(env, this_class, "root", "I");
id_path = (*env)->GetFieldID(env, this_class, "path", "Ljava/lang/String;");
/* get the fields */
root = (HKEY)(*env)->GetIntField(env, this_obj, id_root);
path = (jstring)(*env)->GetObjectField(env, this_obj, id_path);
cpath = (*env)->GetStringUTFChars(env, path, NULL);
/* open the registry key */
if (RegOpenKeyEx(root, cpath, 0, KEY_WRITE, &hkey) != ERROR_SUCCESS)
{
(*env)->ThrowNew(env, (*env)->FindClass(env, "Win32RegKeyException"),
"Open key failed");
(*env)->ReleaseStringUTFChars(env, path, cpath);
return;
}
(*env)->ReleaseStringUTFChars(env, path, cpath);
cname = (*env)->GetStringUTFChars(env, name, NULL);
class_value = (*env)->GetObjectClass(env, value);
class_Integer = (*env)->FindClass(env, "java/lang/Integer");
/* determine the type of the value object */
if ((*env)->IsAssignableFrom(env, class_value, (*env)->FindClass(env, "java/lang/String")))
{
/* it is a string--get a pointer to the characters */
cvalue = (*env)->GetStringUTFChars(env, (jstring) value, NULL);
type = REG_SZ;
size = (*env)->GetStringLength(env, (jstring) value) + 1;
}
else if ((*env)->IsAssignableFrom(env, class_value, class_Integer))
{
/* it is an integer--call intValue to get the value */
jmethodID id_intValue = (*env)->GetMethodID(env, class_Integer, "intValue", "()I");
ivalue = (*env)->CallIntMethod(env, value, id_intValue);
type = REG_DWORD;
cvalue = (char*)&ivalue;
size = 4;
}
else if ((*env)->IsAssignableFrom(env, class_value, (*env)->FindClass(env, "[B")))
{
/* it is a byte array--get a pointer to the bytes */
type = REG_BINARY;
cvalue = (char*)(*env)->GetByteArrayElements(env, (jarray) value, NULL);
size = (*env)->GetArrayLength(env, (jarray) value);
}
else
{
/* we don't know how to handle this type */
(*env)->ThrowNew(env, (*env)->FindClass(env, "Win32RegKeyException"),
"Unsupported value type");
RegCloseKey(hkey);
(*env)->ReleaseStringUTFChars(env, name, cname);
return;
}
/* set the value */
if (RegSetValueEx(hkey, cname, 0, type, cvalue, size) != ERROR_SUCCESS)
{
(*env)->ThrowNew(env, (*env)->FindClass(env, "Win32RegKeyException"),
"Set value failed");
}
RegCloseKey(hkey);
(*env)->ReleaseStringUTFChars(env, name, cname);
/* if the value was a string or byte array, release the pointer */
if (type == REG_SZ)
{
(*env)->ReleaseStringUTFChars(env, (jstring) value, cvalue);
}
else if (type == REG_BINARY)
{
(*env)->ReleaseByteArrayElements(env, (jarray) value, (jbyte*) cvalue, 0);
}
}
/* helper function to start enumeration of names */
static int startNameEnumeration(JNIEnv* env, jobject this_obj, jclass this_class)
{
jfieldID id_index;
jfieldID id_count;
jfieldID id_root;
jfieldID id_path;
jfieldID id_hkey;
jfieldID id_maxsize;
HKEY root;
jstring path;
const char* cpath;
HKEY hkey;
DWORD maxsize = 0;
DWORD count = 0;
/* get the field IDs */
id_root = (*env)->GetFieldID(env, this_class, "root", "I");
id_path = (*env)->GetFieldID(env, this_class, "path", "Ljava/lang/String;");
id_hkey = (*env)->GetFieldID(env, this_class, "hkey", "I");
id_maxsize = (*env)->GetFieldID(env, this_class, "maxsize", "I");
id_index = (*env)->GetFieldID(env, this_class, "index", "I");
id_count = (*env)->GetFieldID(env, this_class, "count", "I");
/* get the field values */
root = (HKEY)(*env)->GetIntField(env, this_obj, id_root);
path = (jstring)(*env)->GetObjectField(env, this_obj, id_path);
cpath = (*env)->GetStringUTFChars(env, path, NULL);
/* open the registry key */
if (RegOpenKeyEx(root, cpath, 0, KEY_READ, &hkey) != ERROR_SUCCESS)
{
(*env)->ThrowNew(env, (*env)->FindClass(env, "Win32RegKeyException"),
"Open key failed");
(*env)->ReleaseStringUTFChars(env, path, cpath);
return -1;
}
(*env)->ReleaseStringUTFChars(env, path, cpath);
/* query count and max length of names */
if (RegQueryInfoKey(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &count, &maxsize,
NULL, NULL, NULL) != ERROR_SUCCESS)
{
(*env)->ThrowNew(env, (*env)->FindClass(env, "Win32RegKeyException"),
"Query info key failed");
RegCloseKey(hkey);
return -1;
}
/* set the field values */
(*env)->SetIntField(env, this_obj, id_hkey, (DWORD) hkey);
(*env)->SetIntField(env, this_obj, id_maxsize, maxsize + 1);
(*env)->SetIntField(env, this_obj, id_index, 0);
(*env)->SetIntField(env, this_obj, id_count, count);
return count;
}
JNIEXPORT jboolean JNICALL Java_Win32RegKeyNameEnumeration_hasMoreElements(JNIEnv* env,
jobject this_obj)
{ jclass this_class;
jfieldID id_index;
jfieldID id_count;
int index;
int count;
/* get the class */
this_class = (*env)->GetObjectClass(env, this_obj);
/* get the field IDs */
id_index = (*env)->GetFieldID(env, this_class, "index", "I");
id_count = (*env)->GetFieldID(env, this_class, "count", "I");
index = (*env)->GetIntField(env, this_obj, id_index);
if (index == -1) /* first time */
{
count = startNameEnumeration(env, this_obj, this_class);
index = 0;
}
else
count = (*env)->GetIntField(env, this_obj, id_count);
return index < count;
}
JNIEXPORT jobject JNICALL Java_Win32RegKeyNameEnumeration_nextElement(JNIEnv* env,
jobject this_obj)
{
jclass this_class;
jfieldID id_index;
jfieldID id_hkey;
jfieldID id_count;
jfieldID id_maxsize;
HKEY hkey;
int index;
int count;
DWORD maxsize;
char* cret;
jstring ret;
/* get the class */
this_class = (*env)->GetObjectClass(env, this_obj);
/* get the field IDs */
id_index = (*env)->GetFieldID(env, this_class, "index", "I");
id_count = (*env)->GetFieldID(env, this_class, "count", "I");
id_hkey = (*env)->GetFieldID(env, this_class, "hkey", "I");
id_maxsize = (*env)->GetFieldID(env, this_class, "maxsize", "I");
index = (*env)->GetIntField(env, this_obj, id_index);
if (index == -1) /* first time */
{
count = startNameEnumeration(env, this_obj, this_class);
index = 0;
}
else
count = (*env)->GetIntField(env, this_obj, id_count);
if (index >= count) /* already at end */
{
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/util/NoSuchElementException"),
"past end of enumeration");
return NULL;
}
maxsize = (*env)->GetIntField(env, this_obj, id_maxsize);
hkey = (HKEY)(*env)->GetIntField(env, this_obj, id_hkey);
cret = (char*)malloc(maxsize);
/* find the next name */
if (RegEnumValue(hkey, index, cret, &maxsize, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
{
(*env)->ThrowNew(env, (*env)->FindClass(env, "Win32RegKeyException"),
"Enum value failed");
free(cret);
RegCloseKey(hkey);
(*env)->SetIntField(env, this_obj, id_index, count);
return NULL;
}
ret = (*env)->NewStringUTF(env, cret);
free(cret);
/* increment index */
index++;
(*env)->SetIntField(env, this_obj, id_index, index);
if (index == count) /* at end */
{
RegCloseKey(hkey);
}
return ret;
}
此时需要使用javah命令来生成对应的头文件,注意如果java代码没有在src的根目录下,有包路径的话javah命令需要在src目录中执行,并指定全路径.此时对应的上述C代码也需要进行响应的修改,如生成后的文件名\参数名等
src>javah Win32RegKey
src>javah Win32RegKeyNameEnumeration
如果修改了包路径为src.a.b.c.Win32RegKey,则需要
src>javah a.b.c.Win32RegKey
此时对应生成两个头文件Win32RegKey.h 及Win32RegKeyNameEnumeration.h
编译DLL
将Win32RegKey.c/Win32RegKey.h/Win32RegKeyNameEnumeration.h三个文件放在同一个路径中,因为是在windows平台编译,所以需要gcc环境.此次遇到一个问题,之前按照书中都是用cygwin的,然后通过gcc -mno-cygwin来去掉最后生成的dll中依赖的cygwin1.dll库.但是gcc更新了,将此参数去掉了,所以本次采用了MinGW-64的环境,如果还需要32位环境需要单独用MinGW来编译.具体安装gcc及相关配置略.
配置好gcc后,进行编译,命令中的jdk是自己机器中使用的jdk的环境的路径
gcc -D __int64="long long" -Wl,--kill-at -I jdk/include -I jdk/include/win32 -shared Win32RegKey.c -o Win32RegKey.dll
此处-D __int64="long long"在书中有具体说明,因为typedef __int64 long 是专门用于微软编译器的,如果使用Gnu编译器可以使用-D __int64="long long"方式.
编译好后,将生成的dll放到jdk/jre/bin目录中,最后运行测试类Win32RegKeyTest.java 即可.
其他代码
Win32RegKey.java
import java.util.Enumeration;
/**
* A Win32RegKey object can be used to get and set values of a registry key in the Windows registry.
*
* @author Cay Horstmann
* @version 1.00 1997-07-01
*/
public class Win32RegKey {
/**
* Construct a registry key object.
*
* @param theRoot one of HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS,
* HKEY_CURRENT_CONFIG, HKEY_DYN_DATA
* @param thePath the registry key path
*/
public Win32RegKey(int theRoot, String thePath) {
root = theRoot;
path = thePath;
}
/**
* Enumerates all names of registry entries under the path that this object describes.
*
* @return an enumeration listing all entry names
*/
public Enumeration<String> names() {
return new Win32RegKeyNameEnumeration(root, path);
}
/**
* Gets the value of a registry entry.
*
* @param name the entry name
* @return the associated value
*/
public native Object getValue(String name);
/**
* Sets the value of a registry entry.
*
* @param name the entry name
* @param value the new value
*/
public native void setValue(String name, Object value);
public static final int HKEY_CLASSES_ROOT = 0x80000000;
public static final int HKEY_CURRENT_USER = 0x80000001;
public static final int HKEY_LOCAL_MACHINE = 0x80000002;
public static final int HKEY_USERS = 0x80000003;
public static final int HKEY_CURRENT_CONFIG = 0x80000005;
public static final int HKEY_DYN_DATA = 0x80000006;
private int root;
private String path;
static {
System.loadLibrary("Win32RegKey");
}
}
class Win32RegKeyNameEnumeration implements Enumeration<String> {
Win32RegKeyNameEnumeration(int theRoot, String thePath) {
root = theRoot;
path = thePath;
}
public native String nextElement();
public native boolean hasMoreElements();
private int root;
private String path;
private int index = -1;
private int hkey = 0;
private int maxsize;
private int count;
}
class Win32RegKeyException extends RuntimeException {
public Win32RegKeyException() {
}
public Win32RegKeyException(String why) {
super(why);
}
}
Win32RegKeyTest.java
import java.util.Enumeration;
/**
* @author Cay Horstmann
* @version 1.02 2007-10-26
*/
public class Win32RegKeyTest {
public static void main(String[] args) {
Win32RegKey key = new Win32RegKey(
Win32RegKey.HKEY_CURRENT_USER, "Software\\JavaSoft\\Java Runtime Environment");
System.out.println(111);
key.setValue("Default user", "Harry Hacker");
key.setValue("Lucky number", new Integer(13));
key.setValue("Small primes", new byte[]{2, 3, 5, 7, 11});
Enumeration<String> e = key.names();
while (e.hasMoreElements()) {
String name = e.nextElement();
System.out.print(name + "=");
Object value = key.getValue(name);
if (value instanceof byte[])
for (byte b : (byte[]) value) System.out.print((b & 0xFF) + " ");
else
System.out.print(value);
System.out.println();
}
}
}