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:
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:
- 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).
- 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:
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.
Comments
Anonymous
June 27, 2006
Did you actually try this? I can't get it to work. First off RegDisableReflectionKey will return AccessDenied if you don't pass true to OpenSubKey. Secondly when I run it calling hiveKey.GetValue(...) after all that I still get the 32-bit value.Anonymous
December 16, 2008
Also tried it and it does not work.Anonymous
February 04, 2009
There's a difference between redirection and reflection (which ironically is a totally different thing to .NET reflection). See see http://msdn.microsoft.com/en-us/library/aa384235%28VS.85%29.aspx Redirection is what prevents 32-bit applications from accessing certain 64-bit-only registry settings. Reflection copies certain redirected keys when they are changed to make it look as if they weren't redirected. You can think of it as hiding redirection, in that if you wrote to a reflected key with a 32-bit application, the setting would be readable by 64-bit applications. This code does not disable redirection of 32-bit applications to 32-bit-only registry keys; it will only disable reflection - automatic copying between 32-bit/64-bit and 64-bit/32-bit. This can be useful, but only in very restricted circumstances. If you want to disable redirection on your registry access, you want part two: http://blogs.msdn.com/cumgranosalis/archive/2005/12/19/Win64RegistryPart2.aspx