/* Copyright (c) 2007 Thomas Boerkel, All Rights Reserved Disclaimer: =========== This code is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ package hudson.util.jna; import com.sun.jna.ptr.IntByReference; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.TreeMap; import java.util.TreeSet; /** * Represents a Win32 registry key. * * @author Kohsuke Kawaguchi */ public class RegistryKey { /** * 32bit Windows key value. */ private int handle; private final RegistryKey root; private final String path; /** * Constructor for the root key. */ private RegistryKey(int handle) { this.handle = handle; root = this; path = ""; } private RegistryKey(RegistryKey ancestor, String path,int handle) { this.handle = handle; this.root = ancestor.root; this.path = combine(ancestor.path,path); } private static String combine(String a, String b) { if(a.length()==0) return b; if(b.length()==0) return a; return a+'\\'+b; } /** * Converts a Windows buffer to a Java String. * * @param buf buffer * @throws java.io.UnsupportedEncodingException on error * @return String */ private static String convertBufferToString(byte[] buf) { try { return new String(buf, 0, buf.length - 2, "UTF-16LE"); } catch (UnsupportedEncodingException e) { throw new AssertionError(e); // impossible } } /** * Converts a Windows buffer to an int. * * @param buf buffer * @return int */ private static int convertBufferToInt(byte[] buf) { return (((int) (buf[0] & 0xff)) + (((int) (buf[1] & 0xff)) << 8) + (((int) (buf[2] & 0xff)) << 16) + (((int) (buf[3] & 0xff)) << 24)); } public String getStringValue(String valueName) { return convertBufferToString(getValue(valueName)); } /** * Read an int value. */ public int getIntValue(String valueName) { return convertBufferToInt(getValue(valueName)); } private byte[] getValue(String valueName) { IntByReference pType, lpcbData; byte[] lpData = new byte[1]; pType = new IntByReference(); lpcbData = new IntByReference(); OUTER: while(true) { int r = Advapi32.INSTANCE.RegQueryValueEx(handle, valueName, null, pType, lpData, lpcbData); switch (r) { case WINERROR.ERROR_MORE_DATA: lpData = new byte[lpcbData.getValue()]; continue OUTER; case WINERROR.ERROR_SUCCESS: return lpData; } throw new JnaException(r); } } public void deleteValue(String valueName) { check(Advapi32.INSTANCE.RegDeleteValue(handle, valueName)); } private void check(int r) { if (r != WINERROR.ERROR_SUCCESS) throw new JnaException(r); } /** * Writes a String value. */ public void setValue(String name, String value) { try { byte[] bytes = value.getBytes("UTF-16LE"); int newLength = bytes.length+2; // for 0 padding byte[] with0 = new byte[newLength]; System.arraycopy(bytes, 0, with0, 0, newLength); check(Advapi32.INSTANCE.RegSetValueEx(handle, name, 0, WINNT.REG_SZ, with0, with0.length)); } catch (UnsupportedEncodingException e) { throw new AssertionError(e); } } /** * Writes a DWORD value. */ public void setValue(String name, int value) { byte[] data = new byte[4]; data[0] = (byte) (value & 0xff); data[1] = (byte) ((value >> 8) & 0xff); data[2] = (byte) ((value >> 16) & 0xff); data[3] = (byte) ((value >> 24) & 0xff); check(Advapi32.INSTANCE.RegSetValueEx(handle, name, 0, WINNT.REG_DWORD, data, data.length)); } /** * Does a specified value exist? */ public boolean valueExists(String name) { int r = Advapi32.INSTANCE.RegQueryValueEx(handle, name, null, new IntByReference(), new byte[1], new IntByReference()); switch(r) { case WINERROR.ERROR_FILE_NOT_FOUND: return false; case WINERROR.ERROR_SUCCESS: return true; default: throw new JnaException(r); } } /** * Deletes this key (and disposes the key.) */ public void delete() { check(Advapi32.INSTANCE.RegDeleteKey(handle, path)); dispose(); } /** * Get all sub keys of a key. * * @return array with all sub key names */ public Collection<String> getSubKeys() { WINBASE.FILETIME lpftLastWriteTime; TreeSet<String> subKeys = new TreeSet<String>(); char[] lpName = new char[256]; IntByReference lpcName = new IntByReference(256); lpftLastWriteTime = new WINBASE.FILETIME(); int dwIndex = 0; while (Advapi32.INSTANCE.RegEnumKeyEx(handle, dwIndex, lpName, lpcName, null, null, null, lpftLastWriteTime) == WINERROR.ERROR_SUCCESS) { subKeys.add(new String(lpName, 0, lpcName.getValue())); lpcName.setValue(256); dwIndex++; } return subKeys; } public RegistryKey open(String subKeyName) { return open(subKeyName,0xF003F/*KEY_ALL_ACCESS*/); } public RegistryKey openReadonly(String subKeyName) { return open(subKeyName,0x20019/*KEY_READ*/); } public RegistryKey open(String subKeyName, int access) { IntByReference pHandle = new IntByReference(); check(Advapi32.INSTANCE.RegOpenKeyEx(handle, subKeyName, 0, access, pHandle)); return new RegistryKey(this,subKeyName,pHandle.getValue()); } /** * Get all values under a key. * * @return TreeMap with name and value pairs */ public TreeMap<String, Object> getValues() { int dwIndex, result; char[] lpValueName; byte[] lpData; IntByReference lpcchValueName, lpType, lpcbData; String name; TreeMap<String, Object> values = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER); lpValueName = new char[16384]; lpcchValueName = new IntByReference(16384); lpType = new IntByReference(); lpData = new byte[1]; lpcbData = new IntByReference(); dwIndex = 0; while (true) { lpcbData.setValue(0); result = Advapi32.INSTANCE.RegEnumValue(handle, dwIndex, lpValueName, lpcchValueName, null, lpType, lpData, lpcbData); switch (result) { case WINERROR.ERROR_NO_MORE_ITEMS: return values; case WINERROR.ERROR_MORE_DATA: lpData = new byte[lpcbData.getValue()]; lpcchValueName = new IntByReference(16384); check(Advapi32.INSTANCE.RegEnumValue(handle, dwIndex, lpValueName, lpcchValueName, null, lpType, lpData, lpcbData)); name = new String(lpValueName, 0, lpcchValueName.getValue()); switch (lpType.getValue()) { case WINNT.REG_SZ: values.put(name, convertBufferToString(lpData)); break; case WINNT.REG_DWORD: values.put(name, convertBufferToInt(lpData)); break; default: break; // not supported yet } default: check(result); } dwIndex++; } } @Override protected void finalize() throws Throwable { super.finalize(); dispose(); } public void dispose() { if(handle!=0) Advapi32.INSTANCE.RegCloseKey(handle); handle = 0; } // // Root keys // public static final RegistryKey CLASSES_ROOT = new RegistryKey(0x80000000); public static final RegistryKey CURRENT_USER = new RegistryKey(0x80000001); public static final RegistryKey LOCAL_MACHINE = new RegistryKey(0x80000002); public static final RegistryKey USERS = new RegistryKey(0x80000003); }