All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jagrosh.discordipc.impl.WinRegistry Maven / Gradle / Ivy

Go to download

Connect locally to the Discord client using IPC for a subset of RPC features like Rich Presence and Activity Join/Spectate

There is a newer version: 0.10.0
Show newest version
/*
 * Copyright 2017 John Grosh ([email protected]).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.jagrosh.discordipc.impl;

import net.lenni0451.reflect.Methods;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.prefs.Preferences;

@SuppressWarnings("NegativeIntConstantInLongContext")
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;

    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 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;

    private static final float javaSpec;

    static {
        try {
            javaSpec = Float.parseFloat(System.getProperty("java.specification.version"));

            regOpenKey = Methods.getDeclaredMethod(userClass, "WindowsRegOpenKey",
                    (javaSpec >= 11 ? long.class : int.class), byte[].class, int.class);
            regCloseKey = Methods.getDeclaredMethod(userClass, "WindowsRegCloseKey",
                    (javaSpec >= 11 ? long.class : int.class));
            regQueryValueEx = Methods.getDeclaredMethod(userClass, "WindowsRegQueryValueEx",
                    (javaSpec >= 11 ? long.class : int.class), byte[].class);
            regEnumValue = Methods.getDeclaredMethod(userClass, "WindowsRegEnumValue",
                    (javaSpec >= 11 ? long.class : int.class), int.class, int.class);
            regQueryInfoKey = Methods.getDeclaredMethod(userClass, "WindowsRegQueryInfoKey1",
                    (javaSpec >= 11 ? long.class : int.class));
            regEnumKeyEx = Methods.getDeclaredMethod(userClass,
                    "WindowsRegEnumKeyEx", (javaSpec >= 11 ? long.class : int.class), int.class,
                    int.class);
            regCreateKeyEx = Methods.getDeclaredMethod(userClass,
                    "WindowsRegCreateKeyEx", (javaSpec >= 11 ? long.class : int.class),
                    byte[].class);
            regSetValueEx = Methods.getDeclaredMethod(userClass,
                    "WindowsRegSetValueEx", (javaSpec >= 11 ? long.class : int.class),
                    byte[].class, byte[].class);
            regDeleteValue = Methods.getDeclaredMethod(userClass,
                    "WindowsRegDeleteValue", (javaSpec >= 11 ? long.class : int.class),
                    byte[].class);
            regDeleteKey = Methods.getDeclaredMethod(userClass,
                    "WindowsRegDeleteKey", (javaSpec >= 11 ? long.class : int.class),
                    byte[].class);
        } catch (Exception e) {
            throw new RuntimeException("Unable to setup registry data", e);
        }
    }

    private WinRegistry() {
    }

    /**
     * Read a value from key and value name
     *
     * @param hkey      HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
     * @param key       The target key
     * @param valueName The target value name
     * @return the value
     * @throws IllegalArgumentException  if hkey is invalid
     * @throws IllegalAccessException    if permissions insufficient
     * @throws InvocationTargetException if underlying method(s) throw(s) an exception
     */
    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 a value from key and value name
     *
     * @param hkey      HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
     * @param key       The target key
     * @param valueName The target value name
     * @return the value
     * @throws IllegalArgumentException  if hkey is invalid
     * @throws IllegalAccessException    if permissions insufficient
     * @throws InvocationTargetException if underlying method(s) throw(s) an exception
     */
    public static String readString(long 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  The target key
     * @return the value name(s) plus the value(s)
     * @throws IllegalArgumentException  if hkey is invalid
     * @throws IllegalAccessException    if permissions insufficient
     * @throws InvocationTargetException if underlying method(s) throw(s) an exception
     */
    public static Map 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  The target key
     * @return the value name(s)
     * @throws IllegalArgumentException  if hkey is invalid
     * @throws IllegalAccessException    if permissions insufficient
     * @throws InvocationTargetException if underlying method(s) throw(s) an exception
     */
    public static List 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  The target key
     * @throws IllegalArgumentException  if hkey is invalid
     * @throws IllegalAccessException    if permissions insufficient
     * @throws InvocationTargetException if underlying method(s) throw an exception
     */
    public static void createKey(int hkey, String key)
            throws IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {
        int[] ret;
        if (hkey == HKEY_LOCAL_MACHINE) {
            ret = createKey(systemRoot, hkey, key);
            Methods.invoke(systemRoot, regCloseKey, ret[0]);
        } else if (hkey == HKEY_CURRENT_USER) {
            ret = createKey(userRoot, hkey, key);
            Methods.invoke(userRoot, regCloseKey, ret[0]);
        } else {
            throw new IllegalArgumentException("hkey=" + hkey);
        }
        if (ret[1] != REG_SUCCESS) {
            throw new IllegalArgumentException("rc=" + ret[1] + "  key=" + key);
        }
    }

    /**
     * Create a key
     *
     * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
     * @param key  The target key
     * @throws IllegalArgumentException  if hkey is invalid
     * @throws IllegalAccessException    if permissions insufficient
     * @throws InvocationTargetException if underlying method(s) throw an exception
     */
    public static void createKey(long hkey, String key)
            throws IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {
        long[] ret;
        if (hkey == HKEY_LOCAL_MACHINE) {
            ret = createKey(systemRoot, hkey, key);
            Methods.invoke(systemRoot, regCloseKey, ret[0]);
        } else if (hkey == HKEY_CURRENT_USER) {
            ret = createKey(userRoot, hkey, key);
            Methods.invoke(userRoot, regCloseKey, 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      HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
     * @param key       The target key
     * @param valueName The target value name
     * @param value     The target value
     * @throws IllegalArgumentException  if hkey is invalid
     * @throws IllegalAccessException    if permissions insufficient
     * @throws InvocationTargetException if underlying method(s) throw an exception
     */
    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);
        }
    }

    /**
     * Write a value in a given key/value name
     *
     * @param hkey      HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
     * @param key       The target key
     * @param valueName The target value name
     * @param value     The target value
     * @throws IllegalArgumentException  if hkey is invalid
     * @throws IllegalAccessException    if permissions insufficient
     * @throws InvocationTargetException if underlying method(s) throw an exception
     */
    public static void writeStringValue
    (long 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 HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
     * @param key  The target key
     * @throws IllegalArgumentException  if hkey is invalid
     * @throws IllegalAccessException    if permissions insufficient
     * @throws InvocationTargetException if underlying method(s) throw an exception
     */
    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  HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
     * @param key   The target key
     * @param value The target value
     * @throws IllegalArgumentException  if hkey is invalid
     * @throws IllegalAccessException    if permissions insufficient
     * @throws InvocationTargetException if underlying method(s) throw an exception
     */
    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 = Methods.invoke(root, regOpenKey, new Object[]{
                hkey, toCstr(key), KEY_ALL_ACCESS});
        if (handles[1] != REG_SUCCESS) {
            return handles[1];  // can be REG_NOTFOUND, REG_ACCESSDENIED
        }
        int rc = Methods.invoke(root, regDeleteValue,
                new Object[]{
                        handles[0], toCstr(value)
                });
        Methods.invoke(root, regCloseKey, handles[0]);
        return rc;
    }

    private static int deleteKey(Preferences root, int hkey, String key)
            throws IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {
        return Methods.invoke(root, regDeleteKey,
                new Object[]{hkey, toCstr(key)});  // 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 = Methods.invoke(root, regOpenKey, new Object[]{
                hkey, toCstr(key), KEY_READ});
        if (handles[1] != REG_SUCCESS) {
            return null;
        }
        byte[] valb = Methods.invoke(root, regQueryValueEx, new Object[]{
                handles[0], toCstr(value)});
        Methods.invoke(root, regCloseKey, handles[0]);
        return (valb != null ? new String(valb).trim() : null);
    }

    private static String readString(Preferences root, long hkey, String key, String value)
            throws IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {
        long[] handles = Methods.invoke(root, regOpenKey, new Object[]{
                hkey, toCstr(key), KEY_READ});
        if (handles[1] != REG_SUCCESS) {
            return null;
        }
        byte[] valb = Methods.invoke(root, regQueryValueEx, new Object[]{
                handles[0], toCstr(value)});
        Methods.invoke(root, regCloseKey, handles[0]);
        return (valb != null ? new String(valb).trim() : null);
    }

    @SuppressWarnings("DuplicatedCode")
    private static Map readStringValues
            (Preferences root, int hkey, String key)
            throws IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {
        HashMap results = new HashMap<>();
        int[] handles = Methods.invoke(root, regOpenKey, new Object[]{
                hkey, toCstr(key), KEY_READ});
        if (handles[1] != REG_SUCCESS) {
            return null;
        }
        int[] info = Methods.invoke(root, regQueryInfoKey,
                new Object[]{handles[0]});

        int count = info[0]; // count
        int maxlen = info[3]; // value length max
        for (int index = 0; index < count; index++) {
            byte[] name = Methods.invoke(root, regEnumValue, new Object[]{
                    handles[0], index, maxlen + 1});
            String value = readString(hkey, key, new String(name));
            results.put(new String(name).trim(), value);
        }
        Methods.invoke(root, regCloseKey, handles[0]);
        return results;
    }

    @SuppressWarnings("DuplicatedCode")
    private static List readStringSubKeys
            (Preferences root, int hkey, String key)
            throws IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {
        List results = new ArrayList<>();
        int[] handles = Methods.invoke(root, regOpenKey, new Object[]{
                hkey, toCstr(key), KEY_READ
        });
        if (handles[1] != REG_SUCCESS) {
            return null;
        }
        int[] info = Methods.invoke(root, regQueryInfoKey,
                new Object[]{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 = Methods.invoke(root, regEnumKeyEx, new Object[]{
                    handles[0], index, maxlen + 1
            });
            results.add(new String(name).trim());
        }
        Methods.invoke(root, regCloseKey, handles[0]);
        return results;
    }

    private static int[] createKey(Preferences root, int hkey, String key)
            throws IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {
        return Methods.invoke(root, regCreateKeyEx,
                new Object[]{hkey, toCstr(key)});
    }

    private static long[] createKey(Preferences root, long hkey, String key)
            throws IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {
        return Methods.invoke(root, regCreateKeyEx,
                new Object[]{hkey, toCstr(key)});
    }

    private static void writeStringValue
            (Preferences root, int hkey, String key, String valueName, String value)
            throws IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {
        int[] handles = Methods.invoke(root, regOpenKey, new Object[]{
                hkey, toCstr(key), KEY_ALL_ACCESS});

        Methods.invoke(root, regSetValueEx,
                handles[0], toCstr(valueName), toCstr(value));
        Methods.invoke(root, regCloseKey, handles[0]);
    }

    private static void writeStringValue
            (Preferences root, long hkey, String key, String valueName, String value)
            throws IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {
        long[] handles = Methods.invoke(root, regOpenKey, new Object[]{
                hkey, toCstr(key), KEY_ALL_ACCESS});

        Methods.invoke(root, regSetValueEx,
                handles[0], toCstr(valueName), toCstr(value));
        Methods.invoke(root, regCloseKey, 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;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy