Read/write to Windows registry using Java

前端 未结 24 1603
日久生厌
日久生厌 2020-11-21 05:45

How is it possible to read/write to the Windows registry using Java?

相关标签:
24条回答
  • 2020-11-21 06:16

    I know this question is old, but it is the first search result on google to "java read/write to registry". Recently I found this amazing piece of code which:

    • Can read/write to ANY part of the registry.
    • DOES NOT USE JNI.
    • DOES NOT USE ANY 3rd PARTY/EXTERNAL APPLICATIONS TO WORK.
    • DOES NOT USE THE WINDOWS API (directly)

    This is pure, Java code.

    It uses reflection to work, by actually accessing the private methods in the java.util.prefs.Preferences class. The internals of this class are complicated, but the class itself is very easy to use.

    For example, the following code obtains the exact windows distribution from the registry:

    String value = WinRegistry.readString (
        WinRegistry.HKEY_LOCAL_MACHINE,                             //HKEY
       "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",           //Key
       "ProductName");                                              //ValueName
        System.out.println("Windows Distribution = " + value);          
    

    Here is the original class. Just copy paste it and it should work:

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.prefs.Preferences;
    
    public class WinRegistry {
      public static final int HKEY_CURRENT_USER = 0x80000001;
      public static final int HKEY_LOCAL_MACHINE = 0x80000002;
      public static final int REG_SUCCESS = 0;
      public static final int REG_NOTFOUND = 2;
      public static final int REG_ACCESSDENIED = 5;
    
      private static final int KEY_ALL_ACCESS = 0xf003f;
      private static final int KEY_READ = 0x20019;
      private static final Preferences userRoot = Preferences.userRoot();
      private static final Preferences systemRoot = Preferences.systemRoot();
      private static final Class<? extends Preferences> userClass = userRoot.getClass();
      private static final Method regOpenKey;
      private static final Method regCloseKey;
      private static final Method regQueryValueEx;
      private static final Method regEnumValue;
      private static final Method regQueryInfoKey;
      private static final Method regEnumKeyEx;
      private static final Method regCreateKeyEx;
      private static final Method regSetValueEx;
      private static final Method regDeleteKey;
      private static final Method regDeleteValue;
    
      static {
        try {
          regOpenKey = userClass.getDeclaredMethod("WindowsRegOpenKey",
              new Class[] { int.class, byte[].class, int.class });
          regOpenKey.setAccessible(true);
          regCloseKey = userClass.getDeclaredMethod("WindowsRegCloseKey",
              new Class[] { int.class });
          regCloseKey.setAccessible(true);
          regQueryValueEx = userClass.getDeclaredMethod("WindowsRegQueryValueEx",
              new Class[] { int.class, byte[].class });
          regQueryValueEx.setAccessible(true);
          regEnumValue = userClass.getDeclaredMethod("WindowsRegEnumValue",
              new Class[] { int.class, int.class, int.class });
          regEnumValue.setAccessible(true);
          regQueryInfoKey = userClass.getDeclaredMethod("WindowsRegQueryInfoKey1",
              new Class[] { int.class });
          regQueryInfoKey.setAccessible(true);
          regEnumKeyEx = userClass.getDeclaredMethod(  
              "WindowsRegEnumKeyEx", new Class[] { int.class, int.class,  
                  int.class });  
          regEnumKeyEx.setAccessible(true);
          regCreateKeyEx = userClass.getDeclaredMethod(  
              "WindowsRegCreateKeyEx", new Class[] { int.class,  
                  byte[].class });  
          regCreateKeyEx.setAccessible(true);  
          regSetValueEx = userClass.getDeclaredMethod(  
              "WindowsRegSetValueEx", new Class[] { int.class,  
                  byte[].class, byte[].class });  
          regSetValueEx.setAccessible(true); 
          regDeleteValue = userClass.getDeclaredMethod(  
              "WindowsRegDeleteValue", new Class[] { int.class,  
                  byte[].class });  
          regDeleteValue.setAccessible(true); 
          regDeleteKey = userClass.getDeclaredMethod(  
              "WindowsRegDeleteKey", new Class[] { int.class,  
                  byte[].class });  
          regDeleteKey.setAccessible(true); 
        }
        catch (Exception e) {
          throw new RuntimeException(e);
        }
      }
    
      private WinRegistry() {  }
    
      /**
       * Read a value from key and value name
       * @param hkey   HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
       * @param key
       * @param valueName
       * @return the value
       * @throws IllegalArgumentException
       * @throws IllegalAccessException
       * @throws InvocationTargetException
       */
      public static String readString(int hkey, String key, String valueName) 
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        if (hkey == HKEY_LOCAL_MACHINE) {
          return readString(systemRoot, hkey, key, valueName);
        }
        else if (hkey == HKEY_CURRENT_USER) {
          return readString(userRoot, hkey, key, valueName);
        }
        else {
          throw new IllegalArgumentException("hkey=" + hkey);
        }
      }
    
      /**
       * Read value(s) and value name(s) form given key 
       * @param hkey  HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
       * @param key
       * @return the value name(s) plus the value(s)
       * @throws IllegalArgumentException
       * @throws IllegalAccessException
       * @throws InvocationTargetException
       */
      public static Map<String, String> readStringValues(int hkey, String key) 
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        if (hkey == HKEY_LOCAL_MACHINE) {
          return readStringValues(systemRoot, hkey, key);
        }
        else if (hkey == HKEY_CURRENT_USER) {
          return readStringValues(userRoot, hkey, key);
        }
        else {
          throw new IllegalArgumentException("hkey=" + hkey);
        }
      }
    
      /**
       * Read the value name(s) from a given key
       * @param hkey  HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
       * @param key
       * @return the value name(s)
       * @throws IllegalArgumentException
       * @throws IllegalAccessException
       * @throws InvocationTargetException
       */
      public static List<String> readStringSubKeys(int hkey, String key) 
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        if (hkey == HKEY_LOCAL_MACHINE) {
          return readStringSubKeys(systemRoot, hkey, key);
        }
        else if (hkey == HKEY_CURRENT_USER) {
          return readStringSubKeys(userRoot, hkey, key);
        }
        else {
          throw new IllegalArgumentException("hkey=" + hkey);
        }
      }
    
      /**
       * Create a key
       * @param hkey  HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
       * @param key
       * @throws IllegalArgumentException
       * @throws IllegalAccessException
       * @throws InvocationTargetException
       */
      public static void createKey(int hkey, String key) 
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        int [] ret;
        if (hkey == HKEY_LOCAL_MACHINE) {
          ret = createKey(systemRoot, hkey, key);
          regCloseKey.invoke(systemRoot, new Object[] { new Integer(ret[0]) });
        }
        else if (hkey == HKEY_CURRENT_USER) {
          ret = createKey(userRoot, hkey, key);
          regCloseKey.invoke(userRoot, new Object[] { new Integer(ret[0]) });
        }
        else {
          throw new IllegalArgumentException("hkey=" + hkey);
        }
        if (ret[1] != REG_SUCCESS) {
          throw new IllegalArgumentException("rc=" + ret[1] + "  key=" + key);
        }
      }
    
      /**
       * Write a value in a given key/value name
       * @param hkey
       * @param key
       * @param valueName
       * @param value
       * @throws IllegalArgumentException
       * @throws IllegalAccessException
       * @throws InvocationTargetException
       */
      public static void writeStringValue
        (int hkey, String key, String valueName, String value) 
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        if (hkey == HKEY_LOCAL_MACHINE) {
          writeStringValue(systemRoot, hkey, key, valueName, value);
        }
        else if (hkey == HKEY_CURRENT_USER) {
          writeStringValue(userRoot, hkey, key, valueName, value);
        }
        else {
          throw new IllegalArgumentException("hkey=" + hkey);
        }
      }
    
      /**
       * Delete a given key
       * @param hkey
       * @param key
       * @throws IllegalArgumentException
       * @throws IllegalAccessException
       * @throws InvocationTargetException
       */
      public static void deleteKey(int hkey, String key) 
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        int rc = -1;
        if (hkey == HKEY_LOCAL_MACHINE) {
          rc = deleteKey(systemRoot, hkey, key);
        }
        else if (hkey == HKEY_CURRENT_USER) {
          rc = deleteKey(userRoot, hkey, key);
        }
        if (rc != REG_SUCCESS) {
          throw new IllegalArgumentException("rc=" + rc + "  key=" + key);
        }
      }
    
      /**
       * delete a value from a given key/value name
       * @param hkey
       * @param key
       * @param value
       * @throws IllegalArgumentException
       * @throws IllegalAccessException
       * @throws InvocationTargetException
       */
      public static void deleteValue(int hkey, String key, String value) 
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        int rc = -1;
        if (hkey == HKEY_LOCAL_MACHINE) {
          rc = deleteValue(systemRoot, hkey, key, value);
        }
        else if (hkey == HKEY_CURRENT_USER) {
          rc = deleteValue(userRoot, hkey, key, value);
        }
        if (rc != REG_SUCCESS) {
          throw new IllegalArgumentException("rc=" + rc + "  key=" + key + "  value=" + value);
        }
      }
    
      // =====================
    
      private static int deleteValue
        (Preferences root, int hkey, String key, String value)
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {
            new Integer(hkey), toCstr(key), new Integer(KEY_ALL_ACCESS) });
        if (handles[1] != REG_SUCCESS) {
          return handles[1];  // can be REG_NOTFOUND, REG_ACCESSDENIED
        }
        int rc =((Integer) regDeleteValue.invoke(root,  
            new Object[] { 
              new Integer(handles[0]), toCstr(value) 
              })).intValue();
        regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) });
        return rc;
      }
    
      private static int deleteKey(Preferences root, int hkey, String key) 
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        int rc =((Integer) regDeleteKey.invoke(root,  
            new Object[] { new Integer(hkey), toCstr(key) })).intValue();
        return rc;  // can REG_NOTFOUND, REG_ACCESSDENIED, REG_SUCCESS
      }
    
      private static String readString(Preferences root, int hkey, String key, String value)
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {
            new Integer(hkey), toCstr(key), new Integer(KEY_READ) });
        if (handles[1] != REG_SUCCESS) {
          return null; 
        }
        byte[] valb = (byte[]) regQueryValueEx.invoke(root, new Object[] {
            new Integer(handles[0]), toCstr(value) });
        regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) });
        return (valb != null ? new String(valb).trim() : null);
      }
    
      private static Map<String,String> readStringValues
        (Preferences root, int hkey, String key)
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        HashMap<String, String> results = new HashMap<String,String>();
        int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {
            new Integer(hkey), toCstr(key), new Integer(KEY_READ) });
        if (handles[1] != REG_SUCCESS) {
          return null;
        }
        int[] info = (int[]) regQueryInfoKey.invoke(root,
            new Object[] { new Integer(handles[0]) });
    
        int count = info[0]; // count  
        int maxlen = info[3]; // value length max
        for(int index=0; index<count; index++)  {
          byte[] name = (byte[]) regEnumValue.invoke(root, new Object[] {
              new Integer
                (handles[0]), new Integer(index), new Integer(maxlen + 1)});
          String value = readString(hkey, key, new String(name));
          results.put(new String(name).trim(), value);
        }
        regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) });
        return results;
      }
    
      private static List<String> readStringSubKeys
        (Preferences root, int hkey, String key)
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        List<String> results = new ArrayList<String>();
        int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {
            new Integer(hkey), toCstr(key), new Integer(KEY_READ) 
            });
        if (handles[1] != REG_SUCCESS) {
          return null;
        }
        int[] info = (int[]) regQueryInfoKey.invoke(root,
            new Object[] { new Integer(handles[0]) });
    
        int count  = info[0]; // Fix: info[2] was being used here with wrong results. Suggested by davenpcj, confirmed by Petrucio
        int maxlen = info[3]; // value length max
        for(int index=0; index<count; index++)  {
          byte[] name = (byte[]) regEnumKeyEx.invoke(root, new Object[] {
              new Integer
                (handles[0]), new Integer(index), new Integer(maxlen + 1)
              });
          results.add(new String(name).trim());
        }
        regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) });
        return results;
      }
    
      private static int [] createKey(Preferences root, int hkey, String key)
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        return  (int[]) regCreateKeyEx.invoke(root,
            new Object[] { new Integer(hkey), toCstr(key) });
      }
    
      private static void writeStringValue 
        (Preferences root, int hkey, String key, String valueName, String value) 
        throws IllegalArgumentException, IllegalAccessException,
        InvocationTargetException 
      {
        int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {
            new Integer(hkey), toCstr(key), new Integer(KEY_ALL_ACCESS) });
    
        regSetValueEx.invoke(root,  
            new Object[] { 
              new Integer(handles[0]), toCstr(valueName), toCstr(value) 
              }); 
        regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) });
      }
    
      // utility
      private static byte[] toCstr(String str) {
        byte[] result = new byte[str.length() + 1];
    
        for (int i = 0; i < str.length(); i++) {
          result[i] = (byte) str.charAt(i);
        }
        result[str.length()] = 0;
        return result;
      }
    }
    

    Original Author: Apache.

    Library Source: https://github.com/apache/npanday/tree/trunk/components/dotnet-registry/src/main/java/npanday/registry

    0 讨论(0)
  • 2020-11-21 06:17

    This was crazy... I took the code from one of the posts here, failed to see there were 18 more comments in which one stated that it does not read a dword value...

    In any case, I've refactored the hell of that code into something with less ifs and methods...

    The Enum could be refined a bit, but as soon as I've fought my way to read a numeric value or byte array and failed, I've given up...

    So here it is:

    package com.nu.art.software.utils;
    
    
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.prefs.Preferences;
    
    /**
     *
     * @author TacB0sS
     */
    public class WinRegistry_TacB0sS {
    
        public static final class RegistryException
                extends Exception {
    
            private static final long serialVersionUID = -8799947496460994651L;
    
            public RegistryException(String message, Throwable e) {
                super(message, e);
            }
    
            public RegistryException(String message) {
                super(message);
            }
    
    
        }
    
        public static final int KEY_WOW64_32KEY = 0x0200;
    
        public static final int KEY_WOW64_64KEY = 0x0100;
    
        public static final int REG_SUCCESS = 0;
    
        public static final int REG_NOTFOUND = 2;
    
        public static final int REG_ACCESSDENIED = 5;
    
        private static final int KEY_ALL_ACCESS = 0xf003f;
    
        private static final int KEY_READ = 0x20019;
    
        public enum WinRegistryKey {
            User(Preferences.userRoot(), 0x80000001), ;
    
            // System(Preferences.systemRoot(), 0x80000002);
    
            private final Preferences preferencesRoot;
    
            private final Integer key;
    
            private WinRegistryKey(Preferences preferencesRoot, int key) {
                this.preferencesRoot = preferencesRoot;
                this.key = key;
            }
        }
    
        private enum WinRegistryMethod {
            OpenKey("WindowsRegOpenKey", int.class, byte[].class, int.class) {
    
                @Override
                protected void verifyReturnValue(Object retValue)
                        throws RegistryException {
                    int[] retVal = (int[]) retValue;
                    if (retVal[1] != REG_SUCCESS)
                        throw new RegistryException("Action Failed, Return Code: " + retVal[1]);
                }
            },
            CreateKeyEx("WindowsRegCreateKeyEx", int.class, byte[].class) {
    
                @Override
                protected void verifyReturnValue(Object retValue)
                        throws RegistryException {
                    int[] retVal = (int[]) retValue;
                    if (retVal[1] != REG_SUCCESS)
                        throw new RegistryException("Action Failed, Return Code: " + retVal[1]);
                }
            },
            DeleteKey("WindowsRegDeleteKey", int.class, byte[].class) {
    
                @Override
                protected void verifyReturnValue(Object retValue)
                        throws RegistryException {
                    int retVal = ((Integer) retValue).intValue();
                    if (retVal != REG_SUCCESS)
                        throw new RegistryException("Action Failed, Return Code: " + retVal);
                }
            },
            DeleteValue("WindowsRegDeleteValue", int.class, byte[].class) {
    
                @Override
                protected void verifyReturnValue(Object retValue)
                        throws RegistryException {
                    int retVal = ((Integer) retValue).intValue();
                    if (retVal != REG_SUCCESS)
                        throw new RegistryException("Action Failed, Return Code: " + retVal);
                }
            },
            CloseKey("WindowsRegCloseKey", int.class),
            QueryValueEx("WindowsRegQueryValueEx", int.class, byte[].class),
            EnumKeyEx("WindowsRegEnumKeyEx", int.class, int.class, int.class),
            EnumValue("WindowsRegEnumValue", int.class, int.class, int.class),
            QueryInfoKey("WindowsRegQueryInfoKey", int.class),
            SetValueEx("WindowsRegSetValueEx", int.class, byte[].class, byte[].class);
    
            private Method method;
    
            private WinRegistryMethod(String methodName, Class<?>... classes) {
                // WinRegistryKey.User.preferencesRoot.getClass().getMDeclaredMethods()
                try {
                    method = WinRegistryKey.User.preferencesRoot.getClass().getDeclaredMethod(methodName, classes);
                } catch (Exception e) {
                    System.err.println("Error");
                    System.err.println(e);
                }
                method.setAccessible(true);
            }
    
            public Object invoke(Preferences root, Object... objects)
                    throws RegistryException {
                Object retValue;
                try {
                    retValue = method.invoke(root, objects);
                    verifyReturnValue(retValue);
                } catch (Throwable e) {
                    String params = "";
                    if (objects.length > 0) {
                        params = objects[0].toString();
                        for (int i = 1; i < objects.length; i++) {
                            params += ", " + objects[i];
                        }
                    }
                    throw new RegistryException("Error invoking method: " + method + ", with params: (" + params + ")", e);
                }
                return retValue;
            }
    
            protected void verifyReturnValue(Object retValue)
                    throws RegistryException {}
        }
    
        private WinRegistry_TacB0sS() {}
    
        public static String readString(WinRegistryKey regKey, String key, String valueName)
                throws RegistryException {
            int retVal = ((int[]) WinRegistryMethod.OpenKey.invoke(regKey.preferencesRoot, regKey.key, toCstr(key),
                    new Integer(KEY_READ)))[0];
    
            byte[] retValue = (byte[]) WinRegistryMethod.QueryValueEx.invoke(regKey.preferencesRoot, retVal,
                    toCstr(valueName));
            WinRegistryMethod.CloseKey.invoke(regKey.preferencesRoot, retVal);
    
            /*
             * Should this return an Empty String.
             */
            return (retValue != null ? new String(retValue).trim() : null);
        }
    
        public static Map<String, String> readStringValues(WinRegistryKey regKey, String key)
                throws RegistryException {
            HashMap<String, String> results = new HashMap<String, String>();
            int retVal = ((int[]) WinRegistryMethod.OpenKey.invoke(regKey.preferencesRoot, regKey.key, toCstr(key),
                    new Integer(KEY_READ)))[0];
    
            int[] info = (int[]) WinRegistryMethod.QueryInfoKey.invoke(regKey.preferencesRoot, retVal);
    
            int count = info[2]; // count
            int maxlen = info[3]; // value length max
            for (int index = 0; index < count; index++) {
                byte[] name = (byte[]) WinRegistryMethod.EnumValue.invoke(regKey.preferencesRoot, retVal,
                        new Integer(index), new Integer(maxlen + 1));
                String value = readString(regKey, key, new String(name));
                results.put(new String(name).trim(), value);
            }
    
            WinRegistryMethod.CloseKey.invoke(regKey.preferencesRoot, retVal);
            return results;
        }
    
        public static List<String> readStringSubKeys(WinRegistryKey regKey, String key)
                throws RegistryException {
            List<String> results = new ArrayList<String>();
            int retVal = ((int[]) WinRegistryMethod.OpenKey.invoke(regKey.preferencesRoot, regKey.key, toCstr(key),
                    new Integer(KEY_READ)))[0];
    
            int[] info = (int[]) WinRegistryMethod.QueryInfoKey.invoke(regKey.preferencesRoot, retVal);
    
            int count = info[0]; // Fix: info[2] was being used here with wrong results. Suggested by davenpcj, confirmed by
                                    // Petrucio
            int maxlen = info[3]; // value length max
            for (int index = 0; index < count; index++) {
                byte[] name = (byte[]) WinRegistryMethod.EnumValue.invoke(regKey.preferencesRoot, retVal,
                        new Integer(index), new Integer(maxlen + 1));
                results.add(new String(name).trim());
            }
    
            WinRegistryMethod.CloseKey.invoke(regKey.preferencesRoot, retVal);
            return results;
        }
    
        public static void createKey(WinRegistryKey regKey, String key)
                throws RegistryException {
            int[] retVal = (int[]) WinRegistryMethod.CreateKeyEx.invoke(regKey.preferencesRoot, regKey.key, toCstr(key));
            WinRegistryMethod.CloseKey.invoke(regKey.preferencesRoot, retVal[0]);
        }
    
        public static void writeStringValue(WinRegistryKey regKey, String key, String valueName, String value)
                throws RegistryException {
            int retVal = ((int[]) WinRegistryMethod.OpenKey.invoke(regKey.preferencesRoot, regKey.key, toCstr(key),
                    new Integer(KEY_ALL_ACCESS)))[0];
    
            WinRegistryMethod.SetValueEx.invoke(regKey.preferencesRoot, retVal, toCstr(valueName), toCstr(value));
            WinRegistryMethod.CloseKey.invoke(regKey.preferencesRoot, retVal);
        }
    
        public static void deleteKey(WinRegistryKey regKey, String key)
                throws RegistryException {
            WinRegistryMethod.DeleteKey.invoke(regKey.preferencesRoot, regKey.key, toCstr(key));
        }
    
        public static void deleteValue(WinRegistryKey regKey, String key, String value)
                throws RegistryException {
            int retVal = ((int[]) WinRegistryMethod.OpenKey.invoke(regKey.preferencesRoot, regKey.key, toCstr(key),
                    new Integer(KEY_ALL_ACCESS)))[0];
            WinRegistryMethod.DeleteValue.invoke(regKey.preferencesRoot, retVal, toCstr(value));
            WinRegistryMethod.CloseKey.invoke(regKey.preferencesRoot, retVal);
        }
    
        // utility
        private static byte[] toCstr(String str) {
            byte[] result = new byte[str.length() + 1];
    
            for (int i = 0; i < str.length(); i++) {
                result[i] = (byte) str.charAt(i);
            }
            result[str.length()] = '\0';
            return result;
        }
    }
    

    NOTE: THIS DOES NOT READ ANYTHING ELSE BUT STRINGS!!!!!

    0 讨论(0)
  • 2020-11-21 06:18

    Yet another library...

    https://code.google.com/p/java-registry/

    This one launches reg.exe under the covers, reading/writing to temporary files. I didn't end up using it, but it looks like a pretty comprehensive implementation. If I did use it, I might dive in and add some better management of the child processes.

    0 讨论(0)
  • 2020-11-21 06:19

    I've done this before using jRegistryKey. It is an LGPL Java/JNI library that can do what you need. Here's an example of how I used it to enabled Registry editing through regedit and also the "Show Folder Options" option for myself in Windows via the registry.

    import java.io.File;
    import ca.beq.util.win32.registry.RegistryKey;
    import ca.beq.util.win32.registry.RegistryValue;
    import ca.beq.util.win32.registry.RootKey;
    import ca.beq.util.win32.registry.ValueType;
    
    
    public class FixStuff {
    
    private static final String REGEDIT_KEY = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
    private static final String REGEDIT_VALUE = "DisableRegistryTools";
    private static final String REGISTRY_LIBRARY_PATH = "\\lib\\jRegistryKey.dll";
    private static final String FOLDER_OPTIONS_KEY = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer";
    private static final String FOLDER_OPTIONS_VALUE = "NoFolderOptions";
    
    public static void main(String[] args) {
        //Load JNI library
        RegistryKey.initialize( new File(".").getAbsolutePath()+REGISTRY_LIBRARY_PATH );
    
        enableRegistryEditing(true);        
        enableShowFolderOptions(true);
    }
    
    private static void enableShowFolderOptions(boolean enable) {
        RegistryKey key = new RegistryKey(RootKey.HKEY_CURRENT_USER,FOLDER_OPTIONS_KEY);
        RegistryKey key2 = new RegistryKey(RootKey.HKEY_LOCAL_MACHINE,FOLDER_OPTIONS_KEY);
        RegistryValue value = new RegistryValue();
        value.setName(FOLDER_OPTIONS_VALUE);
        value.setType(ValueType.REG_DWORD_LITTLE_ENDIAN);
        value.setData(enable?0:1);
    
        if(key.hasValue(FOLDER_OPTIONS_VALUE)) {
            key.setValue(value);
        }
        if(key2.hasValue(FOLDER_OPTIONS_VALUE)) {
            key2.setValue(value);
        }           
    }
    
    private static void enableRegistryEditing(boolean enable) {
        RegistryKey key = new RegistryKey(RootKey.HKEY_CURRENT_USER,REGEDIT_KEY);
        RegistryValue value = new RegistryValue();
        value.setName(REGEDIT_VALUE);
        value.setType(ValueType.REG_DWORD_LITTLE_ENDIAN);
        value.setData(enable?0:1);
    
        if(key.hasValue(REGEDIT_VALUE)) {
            key.setValue(value);
        }
    }
    
    }
    
    0 讨论(0)
  • 2020-11-21 06:20

    Thanks to original post. I have reskinned this utility class and come up over the flaws which it had earlier, thought it might help others so posting here. I have also added some extra utility methods. Now it is able to read any file in windows registry(including REG_DWORD, REG_BINARY, REG_EXPAND_SZ etc.). All the methods work like a charm. Just copy and paste it and it should work. Here is the reskinned and modified class:

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.StringTokenizer;
    import java.util.prefs.Preferences;
    
    public class WinRegistry {
    
        private static final int REG_SUCCESS = 0;
        private static final int REG_NOTFOUND = 2;
        private static final int KEY_READ = 0x20019;
        private static final int REG_ACCESSDENIED = 5;
        private static final int KEY_ALL_ACCESS = 0xf003f;
        public static final int HKEY_CLASSES_ROOT = 0x80000000;
        public static final int HKEY_CURRENT_USER = 0x80000001;
        public static final int HKEY_LOCAL_MACHINE = 0x80000002;
        private static final String CLASSES_ROOT = "HKEY_CLASSES_ROOT";
        private static final String CURRENT_USER = "HKEY_CURRENT_USER";
        private static final String LOCAL_MACHINE = "HKEY_LOCAL_MACHINE";
        private static Preferences userRoot = Preferences.userRoot();
        private static Preferences systemRoot = Preferences.systemRoot();
        private static Class<? extends Preferences> userClass = userRoot.getClass();
        private static Method regOpenKey = null;
        private static Method regCloseKey = null;
        private static Method regQueryValueEx = null;
        private static Method regEnumValue = null;
        private static Method regQueryInfoKey = null;
        private static Method regEnumKeyEx = null;
        private static Method regCreateKeyEx = null;
        private static Method regSetValueEx = null;
        private static Method regDeleteKey = null;
        private static Method regDeleteValue = null;
    
        static {
            try {
                regOpenKey = userClass.getDeclaredMethod("WindowsRegOpenKey", new Class[] {int.class, byte[].class, int.class});
                regOpenKey.setAccessible(true);
                regCloseKey = userClass.getDeclaredMethod("WindowsRegCloseKey", new Class[] {int.class});
                regCloseKey.setAccessible(true);
                regQueryValueEx = userClass.getDeclaredMethod("WindowsRegQueryValueEx", new Class[] {int.class, byte[].class});
                regQueryValueEx.setAccessible(true);
                regEnumValue = userClass.getDeclaredMethod("WindowsRegEnumValue", new Class[] {int.class, int.class, int.class});
                regEnumValue.setAccessible(true);
                regQueryInfoKey = userClass.getDeclaredMethod("WindowsRegQueryInfoKey1", new Class[] {int.class});
                regQueryInfoKey.setAccessible(true);
                regEnumKeyEx = userClass.getDeclaredMethod("WindowsRegEnumKeyEx", new Class[] {int.class, int.class, int.class});  
                regEnumKeyEx.setAccessible(true);
                regCreateKeyEx = userClass.getDeclaredMethod("WindowsRegCreateKeyEx", new Class[] {int.class, byte[].class});  
                regCreateKeyEx.setAccessible(true);
                regSetValueEx = userClass.getDeclaredMethod("WindowsRegSetValueEx", new Class[] {int.class, byte[].class, byte[].class});  
                regSetValueEx.setAccessible(true);
                regDeleteValue = userClass.getDeclaredMethod("WindowsRegDeleteValue", new Class[] {int.class, byte[].class});  
                regDeleteValue.setAccessible(true);
                regDeleteKey = userClass.getDeclaredMethod("WindowsRegDeleteKey", new Class[] {int.class, byte[].class});  
                regDeleteKey.setAccessible(true);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * Reads value for the key from given path
         * @param hkey   HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
         * @param path
         * @param key
         * @return the value
         * @throws IllegalArgumentException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         * @throws IOException 
         */
        public static String valueForKey(int hkey, String path, String key) 
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, IOException {
            if (hkey == HKEY_LOCAL_MACHINE)
                return valueForKey(systemRoot, hkey, path, key);
            else if (hkey == HKEY_CURRENT_USER)
                return valueForKey(userRoot, hkey, path, key);
            else
                return valueForKey(null, hkey, path, key);
        }
    
        /**
         * Reads all key(s) and value(s) from given path
         * @param hkey  HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
         * @param path
         * @return the map of key(s) and corresponding value(s)
         * @throws IllegalArgumentException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         * @throws IOException 
         */
        public static Map<String, String> valuesForPath(int hkey, String path) 
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, IOException {
            if (hkey == HKEY_LOCAL_MACHINE)
                return valuesForPath(systemRoot, hkey, path);
            else if (hkey == HKEY_CURRENT_USER)
                return valuesForPath(userRoot, hkey, path);
            else
                return valuesForPath(null, hkey, path);
        }
    
        /**
         * Read all the subkey(s) from a given path
         * @param hkey  HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
         * @param path
         * @return the subkey(s) list
         * @throws IllegalArgumentException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         */
        public static List<String> subKeysForPath(int hkey, String path)
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
            if (hkey == HKEY_LOCAL_MACHINE)
                return subKeysForPath(systemRoot, hkey, path);
            else if (hkey == HKEY_CURRENT_USER)
                return subKeysForPath(userRoot, hkey, path);
            else
                return subKeysForPath(null, hkey, path);
        }
    
        /**
         * Create a key
         * @param hkey  HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
         * @param key
         * @throws IllegalArgumentException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         */
        public static void createKey(int hkey, String key) 
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
            int [] ret;
            if (hkey == HKEY_LOCAL_MACHINE) {
                ret = createKey(systemRoot, hkey, key);
                regCloseKey.invoke(systemRoot, new Object[] { new Integer(ret[0]) });
            } else if (hkey == HKEY_CURRENT_USER) {
                ret = createKey(userRoot, hkey, key);
                regCloseKey.invoke(userRoot, new Object[] { new Integer(ret[0]) });
            } else
                throw new IllegalArgumentException("hkey=" + hkey);
            if (ret[1] != REG_SUCCESS)
                throw new IllegalArgumentException("rc=" + ret[1] + "  key=" + key);
        }
    
        /**
         * Write a value in a given key/value name
         * @param hkey
         * @param key
         * @param valueName
         * @param value
         * @throws IllegalArgumentException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         */
        public static void writeStringValue(int hkey, String key, String valueName, String value) 
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
            if (hkey == HKEY_LOCAL_MACHINE)
                writeStringValue(systemRoot, hkey, key, valueName, value);
            else if (hkey == HKEY_CURRENT_USER)
                writeStringValue(userRoot, hkey, key, valueName, value);
            else
                throw new IllegalArgumentException("hkey=" + hkey);
        }
    
        /**
         * Delete a given key
         * @param hkey
         * @param key
         * @throws IllegalArgumentException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         */
        public static void deleteKey(int hkey, String key) 
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
            int rc = -1;
            if (hkey == HKEY_LOCAL_MACHINE)
                rc = deleteKey(systemRoot, hkey, key);
            else if (hkey == HKEY_CURRENT_USER)
                rc = deleteKey(userRoot, hkey, key);
            if (rc != REG_SUCCESS)
                throw new IllegalArgumentException("rc=" + rc + "  key=" + key);
        }
    
        /**
         * delete a value from a given key/value name
         * @param hkey
         * @param key
         * @param value
         * @throws IllegalArgumentException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         */
        public static void deleteValue(int hkey, String key, String value) 
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
            int rc = -1;
            if (hkey == HKEY_LOCAL_MACHINE)
                rc = deleteValue(systemRoot, hkey, key, value);
            else if (hkey == HKEY_CURRENT_USER)
                rc = deleteValue(userRoot, hkey, key, value);
            if (rc != REG_SUCCESS)
                throw new IllegalArgumentException("rc=" + rc + "  key=" + key + "  value=" + value);
        }
    
        // =====================
    
        private static int deleteValue(Preferences root, int hkey, String key, String value)
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
            int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {new Integer(hkey), toCstr(key), new Integer(KEY_ALL_ACCESS)});
            if (handles[1] != REG_SUCCESS)
                return handles[1];                                  // can be REG_NOTFOUND, REG_ACCESSDENIED
            int rc =((Integer) regDeleteValue.invoke(root, new Object[] {new Integer(handles[0]), toCstr(value)})).intValue();
            regCloseKey.invoke(root, new Object[] { new Integer(handles[0])});
            return rc;
        }
    
        private static int deleteKey(Preferences root, int hkey, String key) 
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
            int rc =((Integer) regDeleteKey.invoke(root, new Object[] {new Integer(hkey), toCstr(key)})).intValue();
            return rc;                                                  // can REG_NOTFOUND, REG_ACCESSDENIED, REG_SUCCESS
        }
    
        private static String valueForKey(Preferences root, int hkey, String path, String key)
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, IOException {
            int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {new Integer(hkey), toCstr(path), new Integer(KEY_READ)});
            if (handles[1] != REG_SUCCESS)
                throw new IllegalArgumentException("The system can not find the specified path: '"+getParentKey(hkey)+"\\"+path+"'");
            byte[] valb = (byte[]) regQueryValueEx.invoke(root, new Object[] {new Integer(handles[0]), toCstr(key)});
            regCloseKey.invoke(root, new Object[] {new Integer(handles[0])});
            return (valb != null ? parseValue(valb) : queryValueForKey(hkey, path, key));
        }
    
        private static String queryValueForKey(int hkey, String path, String key) throws IOException {
            return queryValuesForPath(hkey, path).get(key);
        }
    
        private static Map<String,String> valuesForPath(Preferences root, int hkey, String path)
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, IOException {
            HashMap<String, String> results = new HashMap<String,String>();
            int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {new Integer(hkey), toCstr(path), new Integer(KEY_READ)});
            if (handles[1] != REG_SUCCESS)
                throw new IllegalArgumentException("The system can not find the specified path: '"+getParentKey(hkey)+"\\"+path+"'");
            int[] info = (int[]) regQueryInfoKey.invoke(root, new Object[] {new Integer(handles[0])});
            int count = info[2];                            // Fixed: info[0] was being used here
            int maxlen = info[4];                           // while info[3] was being used here, causing wrong results
            for(int index=0; index<count; index++) {
                byte[] valb = (byte[]) regEnumValue.invoke(root, new Object[] {new Integer(handles[0]), new Integer(index), new Integer(maxlen + 1)});
                String vald = parseValue(valb);
                if(valb == null || vald.isEmpty())
                    return queryValuesForPath(hkey, path);
                results.put(vald, valueForKey(root, hkey, path, vald));
            }
            regCloseKey.invoke(root, new Object[] {new Integer(handles[0])});
            return results;
        }
    
        /**
         * Searches recursively into the path to find the value for key. This method gives 
         * only first occurrence value of the key. If required to get all values in the path 
         * recursively for this key, then {@link #valuesForKeyPath(int hkey, String path, String key)} 
         * should be used.
         * @param hkey
         * @param path
         * @param key
         * @param list
         * @return the value of given key obtained recursively
         * @throws IllegalArgumentException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         * @throws IOException
         */
        public static String valueForKeyPath(int hkey, String path, String key)
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, IOException {
            String val;
            try {
                val = valuesForKeyPath(hkey, path, key).get(0);
            } catch(IndexOutOfBoundsException e) {
                throw new IllegalArgumentException("The system can not find the key: '"+key+"' after "
                        + "searching the specified path: '"+getParentKey(hkey)+"\\"+path+"'");
            }
            return val;
        }
    
        /**
         * Searches recursively into given path for particular key and stores obtained value in list
         * @param hkey
         * @param path
         * @param key
         * @param list
         * @return list containing values for given key obtained recursively
         * @throws IllegalArgumentException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         * @throws IOException
         */
        public static List<String> valuesForKeyPath(int hkey, String path, String key)
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, IOException {
            List<String> list = new ArrayList<String>();
            if (hkey == HKEY_LOCAL_MACHINE)
                return valuesForKeyPath(systemRoot, hkey, path, key, list);
            else if (hkey == HKEY_CURRENT_USER)
                return valuesForKeyPath(userRoot, hkey, path, key, list);
            else
                return valuesForKeyPath(null, hkey, path, key, list);
        }
    
        private static List<String> valuesForKeyPath(Preferences root, int hkey, String path, String key, List<String> list)
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, IOException {
            if(!isDirectory(root, hkey, path)) {
                takeValueInListForKey(hkey, path, key, list);
            } else {
                List<String> subKeys = subKeysForPath(root, hkey, path);
                for(String subkey: subKeys) {
                    String newPath = path+"\\"+subkey;
                    if(isDirectory(root, hkey, newPath))
                        valuesForKeyPath(root, hkey, newPath, key, list);
                    takeValueInListForKey(hkey, newPath, key, list);
                }
            }
            return list;
        }
    
        /**
         * Takes value for key in list
         * @param hkey
         * @param path
         * @param key
         * @param list
         * @throws IllegalArgumentException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         * @throws IOException
         */
        private static void takeValueInListForKey(int hkey, String path, String key, List<String> list)
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, IOException {
            String value = valueForKey(hkey, path, key);
            if(value != null)
                list.add(value);
        }
    
        /**
         * Checks if the path has more subkeys or not
         * @param root
         * @param hkey
         * @param path
         * @return true if path has subkeys otherwise false
         * @throws IllegalArgumentException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         */
        private static boolean isDirectory(Preferences root, int hkey, String path)
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
            return !subKeysForPath(root, hkey, path).isEmpty();
        }
    
        private static List<String> subKeysForPath(Preferences root, int hkey, String path)
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
            List<String> results = new ArrayList<String>();
            int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {new Integer(hkey), toCstr(path), new Integer(KEY_READ)});
            if (handles[1] != REG_SUCCESS)
                throw new IllegalArgumentException("The system can not find the specified path: '"+getParentKey(hkey)+"\\"+path+"'");
            int[] info = (int[]) regQueryInfoKey.invoke(root, new Object[] {new Integer(handles[0])});
            int count  = info[0]; // Fix: info[2] was being used here with wrong results. Suggested by davenpcj, confirmed by Petrucio
            int maxlen = info[3]; // value length max
            for(int index=0; index<count; index++) {
                byte[] valb = (byte[]) regEnumKeyEx.invoke(root, new Object[] {new Integer(handles[0]), new Integer(index), new Integer(maxlen + 1)});
                results.add(parseValue(valb));
            }
            regCloseKey.invoke(root, new Object[] {new Integer(handles[0])});
            return results;
        }
    
        private static int [] createKey(Preferences root, int hkey, String key)
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
            return (int[]) regCreateKeyEx.invoke(root, new Object[] {new Integer(hkey), toCstr(key)});
        }
    
        private static void writeStringValue(Preferences root, int hkey, String key, String valueName, String value) 
                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
            int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {new Integer(hkey), toCstr(key), new Integer(KEY_ALL_ACCESS)});
            regSetValueEx.invoke(root, new Object[] {new Integer(handles[0]), toCstr(valueName), toCstr(value)}); 
            regCloseKey.invoke(root, new Object[] {new Integer(handles[0])});
        }
    
        /**
         * Makes cmd query for the given hkey and path then executes the query
         * @param hkey
         * @param path
         * @return the map containing all results in form of key(s) and value(s) obtained by executing query
         * @throws IOException
         */
        private static Map<String, String> queryValuesForPath(int hkey, String path) throws IOException {
            String line;
            StringBuilder builder = new StringBuilder();
            Map<String, String> map = new HashMap<String, String>();
            Process process = Runtime.getRuntime().exec("reg query \""+getParentKey(hkey)+"\\" + path + "\"");
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            while((line = reader.readLine()) != null) {
                if(!line.contains("REG_"))
                    continue;
                StringTokenizer tokenizer = new StringTokenizer(line, " \t");
                while(tokenizer.hasMoreTokens()) {
                    String token = tokenizer.nextToken();
                    if(token.startsWith("REG_"))
                        builder.append("\t ");
                    else
                        builder.append(token).append(" ");
                }
                String[] arr = builder.toString().split("\t");
                map.put(arr[0].trim(), arr[1].trim());
                builder.setLength(0);
            }
            return map;
        }
    
        /**
         * Determines the string equivalent of hkey
         * @param hkey
         * @return string equivalent of hkey
         */
        private static String getParentKey(int hkey) {
            if(hkey == HKEY_CLASSES_ROOT)
                return CLASSES_ROOT;
            else if(hkey == HKEY_CURRENT_USER)
                return CURRENT_USER;
            else if(hkey == HKEY_LOCAL_MACHINE)
                return LOCAL_MACHINE;
            return null;
        }
    
        /**
         *Intern method which adds the trailing \0 for the handle with java.dll
         * @param str String
         * @return byte[] 
         */
        private static byte[] toCstr(String str) {
            if(str == null)
                str = "";
            return (str += "\0").getBytes();
        }
    
        /**
         * Method removes the trailing \0 which is returned from the java.dll (just if the last sign is a \0)
         * @param buf the byte[] buffer which every read method returns
         * @return String a parsed string without the trailing \0
         */
        private static String parseValue(byte buf[]) {
            if(buf == null)
                return null;
            String ret = new String(buf);
            if(ret.charAt(ret.length()-1) == '\0')
                return ret.substring(0, ret.length()-1);
            return ret;
        }
    }  
    

    Sample of using the methods is as follows:

    Below method retrieves the value of the key from the given path:

    String hex = WinRegistry.valueForKey(WinRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update", "AUOptions");
    

    This method retrieves all data for the specified path(in form of keys and values) :

    Map<String, String> map = WinRegistry.valuesForPath(WinRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WSMAN");
    

    This method retrieves value recursively for the key from the given path:

    String val = WinRegistry.valueForKeyPath(WinRegistry.HKEY_LOCAL_MACHINE, "System", "TypeID");
    

    and this one retrieves all values recursively for a key from the given path:

    List<String> list = WinRegistry.valuesForKeyPath(
                       WinRegistry.HKEY_LOCAL_MACHINE,                  //HKEY                               "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall",   //path                 "DisplayName"         //Key
                );
    

    Here in above code I retrieved all installed software names in windows system.
    Note: See the documentation of these methods

    And this one retrieves all subkeys of the given path:

    List<String> list3 = WinRegistry.subKeysForPath(WinRegistry.HKEY_CURRENT_USER, "Software");
    

    Important Note: I have modified only reading purpose methods in this process, not the writing purpose methods like createKey, deleteKey etc. They still are same as I recieved them.

    0 讨论(0)
  • 2020-11-21 06:20

    The WinPack Demo has Registry Viewer implemented as an example.

    Check at http://www.jniwrapper.com/winpack_features.jsp#registry

    BTW, WinPack has been moved to the following address:

    http://www.teamdev.com/jniwrapper/winpack/

    0 讨论(0)
提交回复
热议问题