Reading 64 bit registry from 32bit app

By default all registry keys you access from a 32bit application are redirect to the Wow6432Node, and also the other way around, when accessing it from a 64bit program, you only see the 64bit section of the registry.

The normal way of opening a registry key is:

RegistryKey rk = Registry.LocalMachine.OpenSubKey(
    "SOFTWARE\\Microsoft");

EDIT: In .NET 4 you don’t have to do the P/Invoke, just use this instead:

RegistryKey regkey =
    RegistryKey.OpenBaseKey(
        RegistryHive.LocalMachine,
        RegistryView.Registry32)
    .OpenSubKey("SOFTWARE")

and

RegistryKey regkey =
    RegistryKey.OpenBaseKey(
        RegistryHive.LocalMachine,
        RegistryView.Registry64)
    .OpenSubKey("SOFTWARE")

Note that this returns a different section depending on wether the application that’s executing the code has 32 or 64 bit. To use this, you have to use an unmanaged api that can be P/Invoked. We add the following 2 extension methods:

public static RegistryKey Open32BitSubKey(
    this RegistryKey parentKey,
    string subKeyName,
    bool writeable)
public static RegistryKey Open64BitSubKey(
    this RegistryKey parentKey,
    string subKeyName,
    bool writeable)

Native methods required:

static class NativeMethods
{
    [DllImport("advapi32.dll",
        CharSet = CharSet.Unicode,
        EntryPoint = "RegOpenKeyEx")]
    static extern int RegOpenKeyEx(
        IntPtr hKey,
        string subKey,
        uint options,
        int sam,
        out IntPtr phkResult);
    [Flags]
    public enum eRegWow64Options
    {
        None = 0x0000,
        KEY_WOW64_64KEY = 0x0100,
        KEY_WOW64_32KEY = 0x0200,
    }
    [Flags]
    public enum eRegistryRights
    {
        ReadKey = 131097,
        WriteKey = 131078,
    }
    public static RegistryKey Open32BitSubKey(
        this RegistryKey parentKey,
        string subKeyName,
        bool writeable)
    {
        return OpenSubKey(
            parentKey,
            subKeyName,
            writeable,
            eRegWow64Options.KEY_WOW64_32KEY);
    }
    public static RegistryKey Open64BitSubKey(
        this RegistryKey parentKey,
        string subKeyName,
        bool writeable)
    {
        return OpenSubKey(
            parentKey,
            subKeyName,
            writeable,
            eRegWow64Options.KEY_WOW64_64KEY);
    }
    static RegistryKey OpenSubKey(
        RegistryKey parentKey,
        string subKeyName,
        bool writeable,
        eRegWow64Options options)
    {
        if (parentKey == null)
            throw new ArgumentNullException("parentKey");
        IntPtr parentKeyHandle = GetRegistryKeyHandle(parentKey);
        if (parentKeyHandle == IntPtr.Zero)
            throw new ArgumentException("Parent key is not open", "parentKey");
        eRegistryRights rights = eRegistryRights.ReadKey;
        if (writeable)
            rights = eRegistryRights.WriteKey;
        IntPtr subKeyHandle;
        int result = RegOpenKeyEx(
            parentKeyHandle,
            subKeyName,
            0,
            (int)rights | (int)options,
            out subKeyHandle);
        if (result != 0)
            Marshal.ThrowExceptionForHR(result);
        return PointerToRegistryKey(subKeyHandle, writeable, false);
    }
    private static IntPtr GetRegistryKeyHandle(
        RegistryKey pRegisteryKey)
    {
         Type type = Type.GetType("Microsoft.Win32.RegistryKey");
         FieldInfo info = type.GetField("hkey", BindingFlags.NonPublic | BindingFlags.Instance);
         SafeHandle handle = (SafeHandle)info.GetValue(pRegisteryKey);
         return handle.DangerousGetHandle();
    }
    private static RegistryKey PointerToRegistryKey(IntPtr hKey, bool pWritable,
        bool pOwnsHandle)
    {
        if (hKey == IntPtr.Zero)
            return null;
        // Create a SafeHandles.SafeRegistryHandle from this pointer - this is a private class
        BindingFlags privateConstructors = BindingFlags.Instance | BindingFlags.NonPublic;
        Type safeRegistryHandleType = typeof(
            SafeHandleZeroOrMinusOneIsInvalid).Assembly.GetType(
                "Microsoft.Win32.SafeHandles.SafeRegistryHandle");
        Type[] safeRegistryHandleConstructorTypes = new[] { typeof(IntPtr), typeof(Boolean) };
        ConstructorInfo safeRegistryHandleConstructor =
            safeRegistryHandleType.GetConstructor(privateConstructors,
                                                  null, safeRegistryHandleConstructorTypes, null);
        Object safeHandle = safeRegistryHandleConstructor.Invoke(new Object[]
                                                                    {
                                                                        hKey,
                                                                        pOwnsHandle
                                                                    });
        // Create a new Registry key using the private constructor using the
        // safeHandle - this should then behave like
        // a .NET natively opened handle and disposed of correctly
        Type registryKeyType = typeof(RegistryKey);
        Type[] registryKeyConstructorTypes = new[] { safeRegistryHandleType, typeof(Boolean) };
        ConstructorInfo registryKeyConstructor =
            registryKeyType.GetConstructor(privateConstructors, null,
                                           registryKeyConstructorTypes, null);
        RegistryKey result = (RegistryKey)registryKeyConstructor.Invoke(new[] { safeHandle, pWritable });
        return result;
    }
}

To open the 64 bit version of the same section from a 32bit application you use:

Registry.LocalMachine.Open64BitSubKey(
    subKey,
    false);

And to open the 32bit version from a 64bit application:

Registry.LocalMachine.Open32BitSubKey(
    subKey,
    false);