Question

I have a registry HKEY that is openend with RegOpenKeyEx (WinApi). Now I would like to convert the HKEY to the object Microsoft.Win32.RegistryKey. This would allow me to use the more convenient .Net operations to further proceed with this key.

Do you know how this conversion can be done in a reliable way for C# .Net 2.0 not higher?

Thanks for your help!

I tried to use reflection to access RegistryKey.GetBaseKey(hKey) for the conversion from HKEY to RegistryKey but this failed:

[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenKeyEx(IntPtr hKey, string subKey, int ulOptions, int samDesired, out IntPtr phkResult);

public enum RegWow64Options
{
    None = 0,
    KEY_WOW64_64KEY = 0x0100,
    KEY_WOW64_32KEY = 0x0200
}

public enum RegRights
{
    ReadKey = 131097,
    WriteKey = 131078
}

static void exampleTransformKeytoRegistryKey()
{
    IntPtr hKeyChild;
    IntPtr hKeyParent = getRegistryKeyHandle(Registry.LocalMachine);

    if (hKeyParent != IntPtr.Zero)
    {
        int result = RegOpenKeyEx(
            getRegistryKeyHandle(Registry.LocalMachine),
            @"SOFTWARE\Microsoft",
            0,
            ((int)RegRights.ReadKey) | ((int)RegWow64Options.KEY_WOW64_64KEY),
            out hKeyChild);

        if (result == 0)
        {
            // hKeyChild has been retrieved
            // now convert hKeyChild to RegistryKey keyChild

            Type keyType = typeof(RegistryKey);
            RegistryKey keyChild = (RegistryKey)keyType.InvokeMember(
                "GetBaseKey",
                System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static,
                null,
                keyType,
                new object[] { hKeyChild });

            // work with keyChild...
        }
    }
}

static IntPtr getRegistryKeyHandle(RegistryKey registryKey)
{
    Type registryKeyType = typeof(RegistryKey);
    System.Reflection.FieldInfo fieldInfo =
        registryKeyType.GetField("hkey", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

    SafeHandle handle = (SafeHandle)fieldInfo.GetValue(registryKey);
    IntPtr dangerousHandle = handle.DangerousGetHandle();
    return dangerousHandle;
}

Update: The following approach will work (to some degree). Please focus on the function getKeyToRegistryKey.

[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenKeyEx(IntPtr hKey, string subKey, int ulOptions, int samDesired, out IntPtr phkResult);

public enum RegWow64Options
{
    None = 0,
    KEY_WOW64_64KEY = 0x0100,
    KEY_WOW64_32KEY = 0x0200
}

public enum RegRights
{
    ReadKey = 131097,
    WriteKey = 131078
}

static void exampleTransformKeytoRegistryKey2()
{
    IntPtr hKeyChild;
    IntPtr hKeyParent = getRegistryKeyHandle(Registry.LocalMachine);

    if (hKeyParent != IntPtr.Zero)
    {
        int result = RegOpenKeyEx(
            getRegistryKeyHandle(Registry.LocalMachine),
            @"SOFTWARE\Microsoft",
            0,
            ((int)RegRights.ReadKey) | ((int)RegWow64Options.KEY_WOW64_32KEY),
            out hKeyChild);

        if (result == 0)
        {
            // hKeyChild has been retrieved
            // now convert hKeyChild to RegistryKey keyChild
            RegistryKey keyChild = getKeyToRegistryKey(hKeyChild, false, true);

            // work with keyChild...
        }
    }
}

static RegistryKey getKeyToRegistryKey(IntPtr hKey, bool writable, bool ownsHandle)
{
    //Get the BindingFlags for private contructors
    System.Reflection.BindingFlags privateConstructors = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic;
    //Get the Type for the SafeRegistryHandle
    Type safeRegistryHandleType = typeof(Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid).Assembly.GetType("Microsoft.Win32.SafeHandles.SafeRegistryHandle");
    //Get the array of types matching the args of the ctor we want
    Type[] safeRegistryHandleCtorTypes = new Type[] { typeof(IntPtr), typeof(bool) };
    //Get the constructorinfo for our object
    System.Reflection.ConstructorInfo safeRegistryHandleCtorInfo = safeRegistryHandleType.GetConstructor(
            privateConstructors, null, safeRegistryHandleCtorTypes, null);
    //Invoke the constructor, getting us a SafeRegistryHandle
    Object safeHandle = safeRegistryHandleCtorInfo.Invoke(new Object[] { hKey, ownsHandle });

    //Get the type of a RegistryKey
    Type registryKeyType = typeof(RegistryKey);
    //Get the array of types matching the args of the ctor we want
    Type[] registryKeyConstructorTypes = new Type[] { safeRegistryHandleType, typeof(bool) };
    //Get the constructorinfo for our object
    System.Reflection.ConstructorInfo registryKeyCtorInfo = registryKeyType.GetConstructor(
        privateConstructors, null, registryKeyConstructorTypes, null);
    //Invoke the constructor, getting us a RegistryKey
    RegistryKey resultKey = (RegistryKey)registryKeyCtorInfo.Invoke(new Object[] { safeHandle, writable });
    //return the resulting key
    return resultKey;
}

static IntPtr getRegistryKeyHandle(RegistryKey registryKey)
{
    Type registryKeyType = typeof(RegistryKey);
    System.Reflection.FieldInfo fieldInfo =
    registryKeyType.GetField("hkey", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

    SafeHandle handle = (SafeHandle)fieldInfo.GetValue(registryKey);
    IntPtr dangerousHandle = handle.DangerousGetHandle();
    return dangerousHandle;
}

The problem with this approach is that it will only work in pure .Net 2.0 applications. If you use the code in a .Net 2.0 DLL and try to use it from a .Net 4.0 application the code will fail. Thus I still hope to find another solution that works in mixed environments.

Was it helpful?

Solution 2

Given that you target .net 2, there is simply no way out for you. You need to p/invoke this all the way. If you were using .net 4 then you would not need any p/invoke at all because you could use the RegistryView enumeration. But for .net 2 the only option is to p/invoke the native registry APIs.

OTHER TIPS

Use RegistryKey.FromHandle(new SafeRegistryHandle(handle,true));

Its not pretty but its totally doable using reflection. I found this trick in the XSharper source code, and you may try something similar to fit your needs.

public static class RegistryExtensions
{

    public enum RegistryHiveType
    {
        X86,
        X64
    }

    static Dictionary<RegistryHive, UIntPtr> _hiveKeys = new Dictionary<RegistryHive, UIntPtr> {
        { RegistryHive.ClassesRoot, new UIntPtr(0x80000000u) },
        { RegistryHive.CurrentConfig, new UIntPtr(0x80000005u) },
        { RegistryHive.CurrentUser, new UIntPtr(0x80000001u) },
        { RegistryHive.DynData, new UIntPtr(0x80000006u) },
        { RegistryHive.LocalMachine, new UIntPtr(0x80000002u) },
        { RegistryHive.PerformanceData, new UIntPtr(0x80000004u) },
        { RegistryHive.Users, new UIntPtr(0x80000003u) }
    };

    static Dictionary<RegistryHiveType, RegistryAccessMask> _accessMasks = new Dictionary<RegistryHiveType, RegistryAccessMask> {
        { RegistryHiveType.X64, RegistryAccessMask.Wow6464 },
        { RegistryHiveType.X86, RegistryAccessMask.WoW6432 }
    };

    [Flags]
    public enum RegistryAccessMask
    {
        QueryValue          = 0x0001,
        SetValue            = 0x0002,
        CreateSubKey        = 0x0004,
        EnumerateSubKeys    = 0x0008,
        Notify              = 0x0010,
        CreateLink          = 0x0020,
        WoW6432             = 0x0200,
        Wow6464             = 0x0100,
        Write               = 0x20006,
        Read                = 0x20019,
        Execute             = 0x20019,
        AllAccess           = 0xF003F
    }

    [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
    public static extern int RegOpenKeyEx(
      UIntPtr hKey,
      string subKey,
      uint ulOptions,
      uint samDesired,
      out IntPtr hkResult);

    public static RegistryKey OpenBaseKey(RegistryHive registryHive, RegistryHiveType registryType)
    {
        UIntPtr hiveKey = _hiveKeys[registryHive];
        if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major > 5)
        {
            RegistryAccessMask flags = RegistryAccessMask.QueryValue | RegistryAccessMask.EnumerateSubKeys | RegistryAccessMask.SetValue | RegistryAccessMask.CreateSubKey | _accessMasks[registryType];
            IntPtr keyHandlePointer = IntPtr.Zero;
            int result = RegOpenKeyEx(hiveKey, String.Empty, 0, (uint)flags, out keyHandlePointer);
            if (result == 0)
            {
                var safeRegistryHandleType = typeof(SafeHandleZeroOrMinusOneIsInvalid).Assembly.GetType("Microsoft.Win32.SafeHandles.SafeRegistryHandle");
                var safeRegistryHandleConstructor = safeRegistryHandleType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool) }, null); // .NET < 4
                if (safeRegistryHandleConstructor == null)
                    safeRegistryHandleConstructor = safeRegistryHandleType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(IntPtr), typeof(bool) }, null); // .NET >= 4
                var keyHandle = safeRegistryHandleConstructor.Invoke(new object[] { keyHandlePointer, true });
                var net3Constructor = typeof(RegistryKey).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { safeRegistryHandleType, typeof(bool) }, null);
                var net4Constructor = typeof(RegistryKey).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool), typeof(bool), typeof(bool), typeof(bool) }, null);
                object key;
                if (net4Constructor != null)
                    key = net4Constructor.Invoke(new object[] { keyHandlePointer, true, false, false, hiveKey == _hiveKeys[RegistryHive.PerformanceData] });
                else if (net3Constructor != null)
                    key = net3Constructor.Invoke(new object[] { keyHandle, true });
                else
                {
                    var keyFromHandleMethod = typeof(RegistryKey).GetMethod("FromHandle", BindingFlags.Static | BindingFlags.Public, null, new[] { safeRegistryHandleType }, null);
                    key = keyFromHandleMethod.Invoke(null, new object[] { keyHandle });
                }
                var field = typeof(RegistryKey).GetField("keyName", BindingFlags.Instance | BindingFlags.NonPublic);
                if (field != null)
                    field.SetValue(key, String.Empty);
                return (RegistryKey)key;
            }
            else if (result == 2) // The key does not exist.
                return null;
            throw new Win32Exception(result);
        }
        throw new PlatformNotSupportedException("The platform or operating system must be Windows 2000 or later.");
    }
}

Example usage:

var key64 = RegistryExtensions.OpenBaseKey(RegistryHive.LocalMachine, RegistryExtensions.RegistryHiveType.X64);
var key32 = RegistryExtensions.OpenBaseKey(RegistryHive.LocalMachine, RegistryExtensions.RegistryHiveType.X86);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top