As you may know, Windows is virtualizing some parts of the registry under 64 bit.
So if you try to open, for example, this key : “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90″, from a 32 bit C# application running on a 64 bit system, you will be redirected to : “HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server\90″
Why ? Because Windows uses the Wow6432Node registry entry to present a separate view of HKEY_LOCAL_MACHINE\SOFTWARE for 32 bit applications that runs on a 64 bit systems.
If you want to explicitly open the 64 bit view of the registry, here is what you have to perform :
You are using VS 2010 and version 4.x of the .NET framework
It’s really simple, all you need to do is, instead of doing :
//Will redirect you to the 32 bit view RegistryKey sqlsrvKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SQL Server\90");
do the following :
RegistryKey localMachineX64View = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); RegistryKey sqlsrvKey = localMachineX64View.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SQL Server\90");
Prior versions of the .NET framework
For the prior versions of the framework, we have to use P/Invoke and call the function RegOpenKeyExW with parameter KEY_WOW64_64KEY
enum RegWow64Options
{
None = 0,
KEY_WOW64_64KEY = 0x0100,
KEY_WOW64_32KEY = 0x0200
}
enum RegistryRights
{
ReadKey = 131097,
WriteKey = 131078
}
/// <summary>
/// Open a registry key using the Wow64 node instead of the default 32-bit node.
/// </summary>
/// <param name="parentKey">Parent key to the key to be opened.</param>
/// <param name="subKeyName">Name of the key to be opened</param>
/// <param name="writable">Whether or not this key is writable</param>
/// <param name="options">32-bit node or 64-bit node</param>
/// <returns></returns>
static RegistryKey _openSubKey(RegistryKey parentKey, string subKeyName, bool writable, RegWow64Options options)
{
//Sanity check
if (parentKey == null || _getRegistryKeyHandle(parentKey) == IntPtr.Zero)
{
return null;
}
//Set rights
int rights = (int)RegistryRights.ReadKey;
if (writable)
rights = (int)RegistryRights.WriteKey;
//Call the native function >.<
int subKeyHandle, result = RegOpenKeyEx(_getRegistryKeyHandle(parentKey), subKeyName, 0, rights | (int)options, out subKeyHandle);
//If we errored, return null
if (result != 0)
{
return null;
}
//Get the key represented by the pointer returned by RegOpenKeyEx
RegistryKey subKey = _pointerToRegistryKey((IntPtr)subKeyHandle, writable, false);
return subKey;
}
/// <summary>
/// Get a pointer to a registry key.
/// </summary>
/// <param name="registryKey">Registry key to obtain the pointer of.</param>
/// <returns>Pointer to the given registry key.</returns>
static IntPtr _getRegistryKeyHandle(RegistryKey registryKey)
{
//Get the type of the RegistryKey
Type registryKeyType = typeof(RegistryKey);
//Get the FieldInfo of the 'hkey' member of RegistryKey
System.Reflection.FieldInfo fieldInfo =
registryKeyType.GetField("hkey", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
//Get the handle held by hkey
SafeHandle handle = (SafeHandle)fieldInfo.GetValue(registryKey);
//Get the unsafe handle
IntPtr dangerousHandle = handle.DangerousGetHandle();
return dangerousHandle;
}
/// <summary>
/// Get a registry key from a pointer.
/// </summary>
/// <param name="hKey">Pointer to the registry key</param>
/// <param name="writable">Whether or not the key is writable.</param>
/// <param name="ownsHandle">Whether or not we own the handle.</param>
/// <returns>Registry key pointed to by the given pointer.</returns>
static RegistryKey _pointerToRegistryKey(IntPtr hKey, bool writable, bool ownsHandle)
{
//Get the BindingFlags for private contructors
System.Reflection.BindingFlags privateConstructors = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic;
//Get the Type for the SafeRegistryHandle
Type safeRegistryHandleType = typeof(Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid).Assembly.GetType("Microsoft.Win32.SafeHandles.SafeRegistryHandle");
//Get the array of types matching the args of the ctor we want
Type[] safeRegistryHandleCtorTypes = new Type[] { typeof(IntPtr), typeof(bool) };
//Get the constructorinfo for our object
System.Reflection.ConstructorInfo safeRegistryHandleCtorInfo = safeRegistryHandleType.GetConstructor(
privateConstructors, null, safeRegistryHandleCtorTypes, null);
//Invoke the constructor, getting us a SafeRegistryHandle
Object safeHandle = safeRegistryHandleCtorInfo.Invoke(new Object[] { hKey, ownsHandle });
//Get the type of a RegistryKey
Type registryKeyType = typeof(RegistryKey);
//Get the array of types matching the args of the ctor we want
Type[] registryKeyConstructorTypes = new Type[] { safeRegistryHandleType, typeof(bool) };
//Get the constructorinfo for our object
System.Reflection.ConstructorInfo registryKeyCtorInfo = registryKeyType.GetConstructor(
privateConstructors, null, registryKeyConstructorTypes, null);
//Invoke the constructor, getting us a RegistryKey
RegistryKey resultKey = (RegistryKey)registryKeyCtorInfo.Invoke(new Object[] { safeHandle, writable });
//return the resulting key
return resultKey;
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenKeyEx(IntPtr hKey, string subKey, int ulOptions, int samDesired, out int phkResult);
Then we can open our registry key like this :
RegistryKey sqlsrvKey = _openSubKey(Registry.LocalMachine, @"SOFTWARE\Microsoft\Microsoft SQL Server\90", false, RegWow64Options.KEY_WOW64_64KEY);
As you can see, the framework 4 make our life easier
Welcome back to blogging
Nice article. I will hardly use this in the nearest future but was good to know.
Ça va Mehdi?
C’est toi l’auteur de cet article ou ton ami co-blogger?
Hello and thanks for your comment.
C’est son ami co-blogger
This works most of the time. but seems that if the key ALSO exists in the WOW section of the registry, it find and returns thatvalue there first even if you explicitly stated Registry64 This is disappointing and I have yet to find a way around it. It’s a big obtacle if you want to get registry info for SQL Server and you HAD a 32 bit engine installed previously. This is due to ANOTHER flaw in Microsoft software. Their insistence on not cleaning up after themselves in the registry. Ironic since this is one of the requirements for having your software certified for their OS. Gotta love this company
Actually what I posted earlier wasn’t right either. Don’t want to leave bad info hanging out there. After some more testing the real problem seems to be that the Registry64 switch is ignored if the target platform was set to x86. No matter what option you pick, you always get the WOW64 area accessed.
I guess an x86 application can’t read the 64 bit portion of the registry with this .NET4 fix microsoft added. Almost there Microsoft. With .NET4 we can now easily have an x64 bit app read both areas of the registry, but we need an x86 version to be able to do the same
Very quickly this website will be famous amid all blogging and site-building visitors, due to it’s good articles