How to Obtain a RegistryKey Object for the 64-Bit Windows Registry from a 32-Bit Process without .NET 4 or Above

It is totally doable to fetch a RegistryKey object for the 64-bit and 32-bit Windows registries with .NET versions prior to .NET 4. Using reflection this can be achieved as follows, by calling some unexposed constructors that are a part of these frameworks (even previous framework editions contain them, but just don’t necessarily expose them):

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")]
	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, null, 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:

RegistryKey localMachineX64 = RegistryExtensions.OpenBaseKey(RegistryHive.LocalMachine, RegistryExtensions.RegistryHiveType.X64);
RegistryKey localMachineX86 = RegistryExtensions.OpenBaseKey(RegistryHive.LocalMachine, RegistryExtensions.RegistryHiveType.X86);

Once you have a RegistryKey object reference, it should work just fine with the existing RegistryKey methods.

Alexandru

"To avoid criticism, say nothing, do nothing, be nothing." - Aristotle

"It is wise to direct your anger towards problems - not people; to focus your energies on answers - not excuses." - William Arthur Ward

"Science does not know its debt to imagination." - Ralph Waldo Emerson

"Money was never a big motivation for me, except as a way to keep score. The real excitement is playing the game." - Donald Trump

"All our dreams can come true, if we have the courage to pursue them." - Walt Disney

"Mitch flashes back to a basketball game held in the Brandeis University gymnasium in 1979. The team is doing well and chants, 'We're number one!' Morrie stands and shouts, 'What's wrong with being number two?' The students fall silent." - Tuesdays with Morrie

I'm not entirely sure what makes me successful in general programming or development, but to any newcomers to this blood-sport, my best guess would be that success in programming comes from some strange combination of interest, persistence, patience, instincts (for example, someone might tell you that something can't be done, or that it can't be done a certain way, but you just know that can't be true, or you look at a piece of code and know something doesn't seem right with it at first glance, but you can't quite put your finger on it until you think it through some more), fearlessness of tinkering, and an ability to take advice because you should be humble. Its okay to be wrong or to have a bad approach, realize it, and try to find a better one, and even better to be wrong and find a better approach to solve something than to have had a bad approach to begin with. I hope that whatever fragments of information I sprinkle across here help those who hit the same roadblocks.

Leave a Reply

Your email address will not be published. Required fields are marked *