How to access the “real” x64 registry from a Win32 .NET Application – Part I

Jerome, one of our testers, ran into a problem when trying to access the real x64 Windows 2003 server registry from a Win32 managed binary (over the network, no less). The observed behavior of the problem is that, even though a known value is located in the registry (you can see it with regedit), that value is absent when programmatically looking for it.(via the Microsoft.Win32 Registry utility classes).

 

This article describes in some detail the problem and the native solution. If all you need is how to do this in managed, scroll down to the “How do I use this in Managed code?” section.

 

What’s the problem?

The problem is with what’s called “Registry Redirection and Reflection” – that’s a mechanism that makes sure Win32 apps have their own little “sand-boxed” registry area inside the Win64 registry. You can read more about that here:

https://msdn.microsoft.com/library/default.asp?url=/library/en-us/win64/win64/registry_reflection.asp

 

When an app that runs in Win32 compatibility mode (WOW32 – Windows on Windows) needs to access the 64bit registry hive, it cannot do that w/o doing an extra step to instruct Windows to stop redirecting.

 

Generally speaking, there are only two cases where a Win32 app would need to actually access the Win64 registry:

  1. The app needs to stay Win32 (cost of upgrading?) and is some sort of tool that has to do with the registry (directly or indirectly) and thus needs access to the whole thing (think backup app, regedit type apps etc).
  2. The app shares registry information with a 64bit app and thus needs access to the “real” registry.

 

Note: Managed applications written on the .NET 2.0 platform will rarely have problems simply because they would be JITted into Win64 apps. The only cases where this would not be the case is when they rely on Win32 DLLs that don’t yet have Win64 bit versions. In those cases, the dev may ask the JITter to keep app in 32bit mode resulting in no access to the Win64 registry.

What do I do in native code?

Windows 2003 SP1 supplies the developer with tools that will allow a Win32 app to access the Win64 registry w/o redirection. The functions are:

RegDisableReflectionKey

RegEnableReflectionKey and

RegQueryReflectionKey

All three functions are relatively simple to use – in the case of RegDisableReflectionKey, for example, you just pass in an HKEY and that HKEY will no longer be redirected:

 

int result = RegDisableReflectionKey(hkey);

How do I use this in Managed code?

There are two ways to gain access to the real registry in a Win32 managed app. The first is the correct way and as such, is somewhat of a pain in the a.. in the neck.

The only correct way is to use [DllImport] to grab all the Registry functions you need and start using them instead of using the Microsoft.Win32 set of registry classes.

If your code is production code and you want to write something that will keep working after .NET upgrades and not fail miserably with the next Service Pack of .NET 2.0, stop reading here, use P/Invoke to run the registry functionality and be a better man or woman for it.

 

If, however, you want a quick and evil fix for the problem, keep on reading:

 

Still here? Well.. It is a little known fact, but whenever you have code that uses reflection to get around a CLR limitation, somewhere, somehow, a kitten dies. I hope you are proud of yourself. I warned you, my conscious is clear.

 

The first step is to import the RegDisableReflectionKey function. In your class, add the following:

 

[DllImport("advapi32.dll", SetLastError = true)]

static extern int RegDisableReflectionKey(IntPtr hBase);

 

Now, in your code, after opening the registry sub key, do the following:

 

hiveKey = Registry.LocalMachine.OpenSubKey(subKey);

Type type = typeof(RegistryKey);

FieldInfo fi = type.GetField(
"hkey",
BindingFlags.NonPublic | BindingFlags.Instance);

SafeHandle handle = (SafeHandle)fi.GetValue(hiveKey);

IntPtr realHandle = handle.DangerousGetHandle();

int errorCode = RegDisableReflectionKey(handle.DangerousGetHandle());

 

That’s it. You should of course check for errorCode to see if it succeeded, but seeing as how you really don’t care about the lives of kittens, I doubt you really care about error handling.

 

Enjoy.

 

Once we figure out how to do this for remote keys, I will post Part II about that.