• JNI操作Windows注册表(Java核心技术卷2第12章原书例子)


    业务介绍

    最近项目需要通过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.classWin32RegKeyException.classWin32RegKeyNameEnumeration.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();
    		}
    	}
    }
    
    
  • 相关阅读:
    新学期随笔——脚踏实地
    买书方案
    课程总结和建议
    梦断代码阅读笔记03
    梦断代码阅读笔记02
    构建之法阅读笔记06
    【洛谷5284】[十二省联考2019] 字符串问题(后缀树优化建边)
    【BZOJ3514】Codechef MARCH14 GERALD07加强版(LCT_主席树)
    【BZOJ1487】[HNOI2009]无归岛(仙人掌 DP)
    【洛谷3239_BZOJ4008】[HNOI2015] 亚瑟王(期望 DP)
  • 原文地址:https://www.cnblogs.com/GYoungBean/p/13220077.html
Copyright © 2020-2023  润新知