Environment.Is64BitOperatingSystem Alternative for Older .NET Versions

If you are using a version of .NET older than .NET 4, you can use the following logic to check if the current environment is x64 or x86 (this method works almost the same way the .NET framework getter for the Is64BitOperatingSystem flag works; if you don’t believe me, you can use dotPeek to find out for yourself):

public static class EnvironmentHelper
{
    [DllImport("kernel32.dll")]
    static extern IntPtr GetCurrentProcess();

    [DllImport("kernel32.dll")]
    static extern IntPtr GetModuleHandle(string moduleName);

    [DllImport("kernel32.dll")]
    static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

    [DllImport("kernel32.dll")]
    static extern bool IsWow64Process(IntPtr hProcess, out bool wow64Process);

    public static bool Is64BitOperatingSystem()
    {
        // Check if this process is natively an x64 process. If it is, it will only run on x64 environments, thus, the environment must be x64.
        if (IntPtr.Size == 8)
            return true;
        // Check if this process is an x86 process running on an x64 environment.
        IntPtr moduleHandle = GetModuleHandle("kernel32");
        if (moduleHandle != IntPtr.Zero)
        {
            IntPtr processAddress = GetProcAddress(moduleHandle, "IsWow64Process");
            if (processAddress != IntPtr.Zero)
            {
                bool result;
                if (IsWow64Process(GetCurrentProcess(), out result))
                    return result;
            }
        }
        // The environment must be an x86 environment.
        return false;
    }
}

Example usage:

EnvironmentHelper.Is64BitOperatingSystem();

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.