BUTR/Bannerlord.BLSE

View on GitHub
src/Bannerlord.BLSE.Shared/Utils/RegistryHandle.cs

Summary

Maintainability
A
2 hrs
Test Coverage
using Microsoft.Win32.SafeHandles;

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace Bannerlord.BLSE.Shared.Utils;

file static class SafeNativeMethods
{
    internal const int ERROR_SUCCESS = 0;

    internal const int KEY_QUERY_VALUE = 1;
    internal const int KEY_SET_VALUE = 2;
    internal const int KEY_CREATE_SUB_KEY = 4;
    internal const int KEY_ENUMERATE_SUB_KEYS = 8;
    internal const int KEY_NOTIFY = 16;
    internal const int KEY_CREATE_LINK = 32;
    internal const int KEY_READ = 131097;
    internal const int KEY_WRITE = 131078;

    internal const int REG_NONE = 0;
    internal const int REG_SZ = 1;
    internal const int REG_EXPAND_SZ = 2;
    internal const int REG_BINARY = 3;
    internal const int REG_DWORD = 4;
    internal const int REG_DWORD_LITTLE_ENDIAN = 4;
    internal const int REG_DWORD_BIG_ENDIAN = 5;
    internal const int REG_LINK = 6;
    internal const int REG_MULTI_SZ = 7;
    internal const int REG_RESOURCE_LIST = 8;
    internal const int REG_FULL_RESOURCE_DESCRIPTOR = 9;
    internal const int REG_RESOURCE_REQUIREMENTS_LIST = 10;
    internal const int REG_QWORD = 11;

    internal const string ADVAPI32 = "advapi32.dll";

    [DllImport(ADVAPI32, BestFitMapping = false, CharSet = CharSet.Unicode)]
    internal static extern int RegOpenKeyEx(RegistryHandle hKey, string lpSubKey, int ulOptions, int samDesired, out RegistryHandle? hkResult);

    /*
    [DllImport(ADVAPI32, BestFitMapping = false, CharSet = CharSet.Unicode)]
    internal static extern int RegSetValueEx(RegistryHandle hKey, string lpValueName, int Reserved, int dwType, string val, int cbData);

    [DllImport(ADVAPI32, BestFitMapping = false, CharSet = CharSet.Unicode)]
    internal static extern int RegCreateKeyEx(RegistryHandle hKey, string lpSubKey, int Reserved, string? lpClass, int dwOptions, int samDesigner, IntPtr lpSecurityAttributes, out RegistryHandle hkResult, out int lpdwDisposition);
    */

    [DllImport(ADVAPI32)]
    internal static extern int RegCloseKey(IntPtr handle);

    [DllImport(ADVAPI32, BestFitMapping = false, CharSet = CharSet.Unicode)]
    internal static extern int RegQueryValueEx(RegistryHandle hKey, string lpValueName, int lpReserved, ref int lpType, [Out] byte[]? lpData, ref int lpcbData);

    /*
    [DllImport(ADVAPI32, BestFitMapping = false, CharSet = CharSet.Unicode)]
    internal static extern int RegDeleteKey(RegistryHandle hKey, string lpValueName);
    */
}

internal class RegistryHandle : SafeHandleZeroOrMinusOneIsInvalid
{
    public static readonly RegistryHandle HKEY_CLASSES_ROOT = new(new IntPtr(int.MinValue), ownHandle: false);
    public static readonly RegistryHandle HKEY_CURRENT_USER = new(new IntPtr(-2147483647), ownHandle: false);
    public static readonly RegistryHandle HKEY_LOCAL_MACHINE = new(new IntPtr(-2147483646), ownHandle: false);
    public static readonly RegistryHandle HKEY_USERS = new(new IntPtr(-2147483645), ownHandle: false);
    public static readonly RegistryHandle HKEY_PERFORMANCE_DATA = new(new IntPtr(-2147483644), ownHandle: false);
    public static readonly RegistryHandle HKEY_CURRENT_CONFIG = new(new IntPtr(-2147483643), ownHandle: false);
    public static readonly RegistryHandle HKEY_DYN_DATA = new(new IntPtr(-2147483642), ownHandle: false);

    public static int TryGetHKLMSubkey(string key, out RegistryHandle? regHandle)
    {
        return SafeNativeMethods.RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, SafeNativeMethods.KEY_READ, out regHandle);
    }

    public static RegistryHandle? GetHKLMSubkey(string key)
    {
        if (SafeNativeMethods.RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, SafeNativeMethods.KEY_READ, out var hkResult) != SafeNativeMethods.ERROR_SUCCESS || hkResult == null || hkResult.IsInvalid)
            return null;
        return hkResult;
    }

    public static RegistryHandle? GetHKCUSubkey(string key)
    {
        if (SafeNativeMethods.RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, SafeNativeMethods.KEY_READ, out var hkResult) != SafeNativeMethods.ERROR_SUCCESS || hkResult == null || hkResult.IsInvalid)
            return null;
        return hkResult;
    }

    public RegistryHandle(IntPtr hKey, bool ownHandle) : base(ownHandle) => handle = hKey;
    public RegistryHandle() : base(ownsHandle: true) { }

    /*
    public bool DeleteKey(string key)
    {
        if (SafeNativeMethods.RegDeleteKey(this, key) == SafeNativeMethods.ERROR_SUCCESS)
        {
            return true;
        }
        return false;
    }

    public RegistryHandle? CreateSubKey(string subKey)
    {
        if (SafeNativeMethods.RegCreateKeyEx(this, subKey, 0, null, 0, SafeNativeMethods.KEY_CREATE_SUB_KEY, IntPtr.Zero, out var hkResult, out _) != SafeNativeMethods.ERROR_SUCCESS || hkResult == null || hkResult.IsInvalid)
            return null;
        return hkResult;
    }

    public bool SetValue(string valName, string value)
    {
        if (SafeNativeMethods.RegSetValueEx(this, valName, 0, SafeNativeMethods.REG_SZ, value, value.Length * 2 + 2) != SafeNativeMethods.ERROR_SUCCESS)
            return false;
        return true;
    }
    */

    public string? GetStringValue(string valName)
    {
        var lpType = 0;
        var lpcbData = 0;
        if (SafeNativeMethods.RegQueryValueEx(this, valName, 0, ref lpType, null, ref lpcbData) == SafeNativeMethods.ERROR_SUCCESS && lpType == 1)
        {
            var array = new byte[lpcbData];
            var num = SafeNativeMethods.RegQueryValueEx(this, valName, 0, ref lpType, array, ref lpcbData);
            return Encoding.Unicode.GetString(array, 0, array.Length - 2);
        }
        return null;
    }

    public int? GetDwordValue(string valName)
    {
        var lpType = 4;
        var arraySize = 4;
        var array = new byte[arraySize];
        if (SafeNativeMethods.RegQueryValueEx(this, valName, 0, ref lpType, array, ref arraySize) == SafeNativeMethods.ERROR_SUCCESS && lpType == 4)
            return BitConverter.ToInt32(array, 0);
        return null;
    }

    protected override bool ReleaseHandle() => SafeNativeMethods.RegCloseKey(handle) == SafeNativeMethods.ERROR_SUCCESS;
}