This is a continuation of a previous post. As you recall (or not), we had some test code that would open a remote registry on a server machine and read values. These values would then be used on the client machine to run tests against that server. We hit a wall when the OS was x64 because of registry redirection done by Win64 when running in Win32 compatibility mode.
In the previous post, I showed how we were able to solve the problem for a case where the calls are made locally – we used the RegDisableReflectionKey API function to make sure we get access to the correct key. That API call, however, does not work on keys opened on remote machines.
There’s a solution for remote registry calls as well – with 2k3 SP2, MS also introduced two flags that can be used when opening a registry key to specify which registry you wish to open (the 64bit one, or the 32bit one).
The flags are:
KEY_WOW64_32KEY and
KEY_WOW64_64KEY
Now, these flags need to be passed in whenever you call one of these functions:
RegCreateKeyEx
RegDeleteKeyEx
RegOpenKeyEx
As described in the previous post, since managed code does not support these flags, the correct solution is to rewrite all your registry code using native P/Invokes – wrap the registry functionality you are interested in with classes, using SafeHandles and you should be gold.
That said, you can also work around this by using reflection to directly muck with the .NET Registry class. I highly suggest you do not use this method since it will break at some point. It can be the next Service Pack, it can be the next version of .NET, but it will break and you will not get a compiler warning about it. Remember: Every time you work around a .NET limitation by using reflection, somewhere, somehow, a kitten dies.
Following are the steps that will make the Microsoft.Win32.Registry class able to access a 64bit registry from a 32bit program:
/// <summary>
/// This class is an example of how to access the 64bit registry remotely from a 32bit machine.
/// It is important to note that this is a HACK - it will probably stop working when Whidbey SP1 comes out
/// and even if it wont stop working then, it will at some point.
/// Also, the example is not the safest example in the world. Were you to hit an exception after calling the
/// OpenKey function but before plugging it into the SafeHandle, you will leak the handle.
/// </summary>
class Program
{
public const int STANDARD_RIGHTS_REQUIRED = 0x000F0000;
public const int READ_CONTROL = 0x00020000;
public const int SYNCHRONIZE = 0x00100000;
public const int STANDARD_RIGHTS_READ = READ_CONTROL;
public const int STANDARD_RIGHTS_WRITE = READ_CONTROL;
public const int STANDARD_RIGHTS_EXECUTE = READ_CONTROL;
public const int STANDARD_RIGHTS_ALL = 0x001F0000;
public const int KEY_QUERY_VALUE = 0x0001;
public const int KEY_SET_VALUE = 0x0002;
public const int KEY_CREATE_SUB_KEY = 0x0004;
public const int KEY_ENUMERATE_SUB_KEYS = 0x0008;
public const int KEY_NOTIFY = 0x0010;
public const int KEY_CREATE_LINK = 0x0020;
public const int KEY_WOW64_32KEY = 0x0200;
public const int KEY_WOW64_64KEY = 0x0100;
public const int KEY_ALL_ACCESS = STANDARD_RIGHTS_ALL | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_CREATE_LINK & (~SYNCHRONIZE);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "RegOpenKeyEx")]
static extern int RegOpenKeyEx(
IntPtr hKey,
string subKey,
uint options,
int sam,
out IntPtr phkResult);
static void Main(string[] args)
FieldInfo hkeyInfo = GetHkeyField();
RegistryKey remoteKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, "[MachineName]");
SafeHandle remoteKeyHandle = (SafeHandle)hkeyInfo.GetValue(remoteKey);
IntPtr hRemoteKey = remoteKeyHandle.DangerousGetHandle();
IntPtr hTargetKey = IntPtr.Zero;
int l = RegOpenKeyEx(hRemoteKey, @"[Your Key Here]", 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, out hTargetKey);
SafeHandle sh = CreateRegistrySafeHandle(hTargetKey);
RegistryKey targetKey = (RegistryKey)Activator.CreateInstance(typeof(RegistryKey), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { sh, true }, null);
Console.WriteLine(targetKey.GetValue("Location"));
}
private static SafeHandle CreateRegistrySafeHandle(IntPtr handle)
Assembly ass = typeof(SafeHandle).Assembly;
Type type = ass.GetType("Microsoft.Win32.SafeHandles.SafeRegistryHandle");
SafeHandle sh = (SafeHandle)Activator.CreateInstance(type, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { handle, true }, null);
return sh;
private static FieldInfo GetHkeyField()
Type type = typeof(RegistryKey);
FieldInfo info = type.GetField("hkey", BindingFlags.NonPublic | BindingFlags.Instance);
return info;