Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are some alternatives to RegistryKey.OpenBaseKey in .NET 3.5?

I've been working on an installer package and using RegistryKey.OpenBaseKey to work with custom actions that either open and add/remove keys to/from the 64-bit registry or the 32-bit registry from an MSI package, but this requires me to install .NET Framework 4 on the target machine before running my installer, using a bootstrapper or something else, because OpenBaseKey was only introduced in .NET Framework 4. Ideally, I would like to target just .NET Framework 3.5 and still be able to modify either the 64-bit or 32-bit registry hives like in OpenBaseKey; then I wouldn't need .NET 4 and the overhead of installing it.

Are there alternatives to OpenBaseKey for those of us who would not like to make .NET 4 a prerequisite? Something like P/Invoking a certain WinAPI method to kick this off, perhaps? I'm not sure what it would take.

like image 391
Alexandru Avatar asked Oct 06 '14 13:10

Alexandru


3 Answers

Targeting needed registry:

RegistryKey localMachine = Registry.LocalMachine; //For example

and get the value that you need:

RegistryKey lKey = localMachine.OpenSubKey(@"SOFTWARE\...\", false);
like image 197
STG Avatar answered Nov 12 '22 19:11

STG


For anyone who is interested in a C# solution for some of the previous versions of .NET in order to not have to refactor too much code, its not pretty but here it is, totally doable using reflection. I found this trick in the XSharper source code.

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 XP or later.");
    }
}

Example usage:

var key64 = RegistryExtensions.OpenBaseKey(RegistryHive.LocalMachine, RegistryExtensions.RegistryHiveType.X64);
var key32 = RegistryExtensions.OpenBaseKey(RegistryHive.LocalMachine, RegistryExtensions.RegistryHiveType.X86);
like image 19
Alexandru Avatar answered Nov 12 '22 18:11

Alexandru


For .NET versions earlier than version 4 there is no framework API that allows access to alternate registry views. In order to access alternate views you must call the native API RegOpenKeyEx passing one of the KEY_WOW64_32KEY or KEY_WOW64_64KEY flags as appropriate.

Common ways to do this are with C++/CLI mixed mode assemblies, or using P/Invoke. However, this is not very much fun at all. The registry APIs are some of the more awkward to use, because they support multiple data types for values.

like image 2
David Heffernan Avatar answered Nov 12 '22 20:11

David Heffernan