/******************************************************************************* * Copyright (c) 2008, 2011 Thomas Holland (thomas@innot.de) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Enrico Ehrich - initial API and implementation * Thomas Holland - Modified for AVR Plugin * *******************************************************************************/ package de.innot.avreclipse.core.paths.win32; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.eclipse.cdt.utils.WindowsRegistry; /** * This is an extension to the CDT WindowRegistry class. * <p> * Currently the original CDT WindowsRegistry class has some problems on 64bit windows systems, * where it (sometimes?) fails to load its associated dll. * </p> * <p> * This class, which is a (partial, see below) drop in replacement for <code>WindowsRegistry</code> * and will, whenever <code>WindowsRegistry</code> fails, use an alternative method to access the * registry. * </p> * <p> * Instead of using the JNI it will start the Windows <em>'reg query'</em> command and parse its * output. In addition to that it will also automatically look in the '\Wow6432Node' subnode if a * key can not be found. * </p> * <p> * Currently only the methods {@link #getLocalMachineValue(String, String)} and * {@link #getLocalMachineValueName(String, int)} are implemented, because they are the only ones * used by the AVR Eclipse Plugin. * </p> * * @see org.eclipse.cdt.utils.WindowsRegistry * @author Enrico Ehrich * @author Thomas Holland * @noextend This class is not intended to be subclassed by clients. * @noinstantiate This class is not intended to be instantiated by clients. * @since 2.3.2 * */ public class MyWindowsRegistry { /** * Small container to return the results of a query. */ protected static class RegistryKeyValue { public String key; // Key name public String type; // Registry type, e.g. REG_SZ or REG_EXPANDED_SZ public String value; // Key value } /** * Small class that reads all incoming chars from the InputStream and stores them in a String. */ protected static class StreamReader extends Thread { private InputStream is; private StringBuilder sb; protected StreamReader(InputStream is) { this.is = is; sb = new StringBuilder(); } public void run() { try { int c; while ((c = is.read()) != -1) sb.append((char) c); } catch (IOException e) { ; } } /** * Gets the content of the given InputStream. * <p> * As a convenience the content is split into separate lines. * </p> * * @return All lines from the InputStream */ protected String[] getResult() { String result = sb.toString(); // This works only on Windows, but this class is Window specific anyway so we can get // away with this simplistic method. return result.split("\r\n"); } } /** The Windows executable to query the Registry */ private static final String REGQUERY_UTIL = "reg query"; /** Start of the Registry value type. */ private static final String REGTYPE_TOKEN = "REG_"; private static MyWindowsRegistry fInstance; private static WindowsRegistry fCDTRegistryInstance; /** Flag to inhibit calls to the CDT WindowsRegistry class. Used for test purposes. */ private boolean fInhibitOriginal = true; /** * Get the singleton instance of this class. * * @return <code>MyWindowsRegistry</code> instance. */ public static MyWindowsRegistry getRegistry() { if (fInstance == null) { fInstance = new MyWindowsRegistry(); } if (fCDTRegistryInstance == null) { fCDTRegistryInstance = WindowsRegistry.getRegistry(); } return fInstance; } /** * Inhibit usage of the original CDT WindowsRegistry class, always use the fallback method. * <p> * This method is intended only for testing this class. * </p> * * @param inhibit * When <code>true</code> only the fallback method is used (external call to the * 'reg' executable. */ protected void setInhibitOriginal(boolean inhibit) { fInhibitOriginal = inhibit; } /** * @see WindowsRegistry#getLocalMachineValue(String, String) */ public String getKeyValue(String subkey, String name) { String result; // First try the CDT WindowsRegistry Class if (fCDTRegistryInstance != null && !fInhibitOriginal) { // remove HKLM / HKEY_LOCAL_MACHINE from key // this gets added by getLacalMachineValue String key = subkey.replaceFirst("HKLM\\\\", ""); key = key.replaceFirst("HKEY_LOCAL_MACHINE\\\\", ""); result = fCDTRegistryInstance.getLocalMachineValue(key, name); if (result != null) { // Original WindowsRegistry class was successful return result; } } // Original CDT WindowsRegistry failed: Try the fallback RegistryKeyValue[] results; String[] testkeys = convertkeys(subkey); for (String k : testkeys) { String parameters = "\"" + k + "\" /v " + name; results = executeKeyValueCommand(parameters); if (results.length > 0) { return results[0].value; } } return null; } /** * @see WindowsRegistry#getLocalMachineValueName(String, int) */ public String getKeyName(String subkey, int index) { String result; // First try the CDT WindowsRegistry Class if (fCDTRegistryInstance != null && !fInhibitOriginal) { // remove HKLM / HKEY_LOCAL_MACHINE from key // this gets added by getLacalMachineValue String key = subkey.replaceFirst("HKLM\\\\", ""); key = key.replaceFirst("HKEY_LOCAL_MACHINE\\\\", ""); result = fCDTRegistryInstance.getLocalMachineValueName(key, index); if (result != null) { // Original WindowsRegistry class was successful return result; } } // Original WindowsRegistry failed: Try the fallback RegistryKeyValue[] results; String[] testkeys = convertkeys(subkey); for (String k : testkeys) { String parameters = "\"" + k + "\" /s"; results = executeKeyValueCommand(parameters); if (results.length > index) { return results[index].key; } } return null; } public List<String> getSubkeys(String key) { List<String> subkeys = new ArrayList<String>(); // First try the CDT WindowsRegistry Class if (fCDTRegistryInstance != null && !fInhibitOriginal) { // remove HKLM / HKEY_LOCAL_MACHINE from key // this gets added by getLacalMachineValue String subkey = key.replaceFirst("HKLM\\\\", ""); subkey = subkey.replaceFirst("HKEY_LOCAL_MACHINE\\\\", ""); int i = 0; String nextkey = null; do { nextkey = fCDTRegistryInstance.getLocalMachineValueName(subkey, i); if (nextkey != null) { subkeys.add(nextkey); i++; } } while (nextkey != null); if (subkeys.size() != 0) { // Original WindowsRegistry class was successful return subkeys; } } // Original WindowsRegistry failed: Try the fallback subkeys = executeSubKeysCommand("\"" + key + "\""); if (subkeys.size() == 0) { // Try Win64 location String key32 = key.replaceFirst("SOFTWARE", "SOFTWARE\\\\Wow6432Node"); subkeys = executeSubKeysCommand("\"" + key32 + "\""); } return subkeys; } /** * Executes "reg query" with the given parameter string, parses the output and returns an array * of {@link RegistryKeyValue} objects. If the call fails in any way an empty array is returned. * * @param parameter * for the "reg query" call * @return array of Key/Value objects. The array may be empty, but never <code>null</code>. */ private RegistryKeyValue[] executeKeyValueCommand(String parameter) { List<RegistryKeyValue> results = new ArrayList<RegistryKeyValue>(); String[] alllines = executeRegCommand(parameter); for (String line : alllines) { if (line.indexOf(REGTYPE_TOKEN) != -1) { // line contains "REG_" // split it into key, type, and value String[] items; // Problem: 'reg query' on win32 separtes key/type/value with tabs // on win64 they are separated by four spaces. String trimmedline = line.trim(); if (trimmedline.contains("\t")) { items = trimmedline.split("\t"); } else if (trimmedline.contains(" ")) { items = trimmedline.split(" "); } else { // not field separator found break; } if (items.length >= 3) { RegistryKeyValue keyvalue = new RegistryKeyValue(); keyvalue.key = items[0].trim(); keyvalue.type = items[1].trim(); keyvalue.value = items[2].trim(); results.add(keyvalue); } } } return results.toArray(new RegistryKeyValue[results.size()]); } /** * Executes "reg query" with the given parameter string, parses the output and returns an array * of subkey Strings. If the call fails in any way an empty array is returned. * * @param parameter * for the "reg query" call * @return array of String objects. The array may be empty, but never <code>null</code>. */ private List<String> executeSubKeysCommand(String parameter) { List<String> allkeys = new ArrayList<String>(); String[] alllines = executeRegCommand(parameter); for (String line : alllines) { if (line.indexOf("HKEY_") != -1) { allkeys.add(line); } } return allkeys; } private String[] executeRegCommand(String parameter) { String command = REGQUERY_UTIL + " " + parameter; String[] result = {}; try { Process process = Runtime.getRuntime().exec(command); StreamReader reader = new StreamReader(process.getInputStream()); reader.start(); process.waitFor(); reader.join(); result = reader.getResult(); } catch (IOException e) { // In case of an exception we return what we have found so far (which may be nothing = // empty array) } catch (InterruptedException e) { // In case of an exception we return what we have found so far (which may be nothing = // empty array) } return result; } private String[] convertkeys(String key) { String key32 = key.replaceFirst("SOFTWARE", "SOFTWARE\\\\Wow6432Node"); String[] allkeys = new String[2]; allkeys[0] = key; allkeys[1] = key32; return allkeys; } }