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.
Targeting needed registry:
RegistryKey localMachine = Registry.LocalMachine; //For example
and get the value that you need:
RegistryKey lKey = localMachine.OpenSubKey(@"SOFTWARE\...\", false);
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);
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With