I have a c# unit test project that is compiled for AnyCPU. Our build server is a 64bit machine, and has a 64bit SQL Express instance installed.
The test project uses code similar to the following to identify the path to the .MDF files:
private string GetExpressPath()
{
RegistryKey sqlServerKey = Registry.LocalMachine.OpenSubKey( @"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL" );
string sqlExpressKeyName = (string) sqlServerKey.GetValue( "SQLEXPRESS" );
RegistryKey sqlInstanceSetupKey = sqlServerKey.OpenSubKey( sqlExpressKeyName + @"\Setup" );
return sqlInstanceSetupKey.GetValue( "SQLDataRoot" ).ToString();
}
This code works fine on our 32bit workstations, and did work ok on the build server until I recently enabled code coverage analysis with NCover. Because NCover uses a 32bit COM component, the test runner (Gallio) runs as a 32bit process.
Checking the registry, there is no "Instance Names" key under
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server
Is there a way for an application running in 32bit mode to access the registry outside Wow6432Node?
Best Answer
Reading the 64 bit registry is possible because of WOW64 which is a Windows subsystem providing access to 64 bit from within 32 bit applications. (Likewise, in older NT-based Windows versions it was called WOW and was an emulation layer inside 32 bit Windows to support 16 bit applications).
There is still native support for registry access under 64 bit Windows using .NET Framework 4.x and for newer .NET versions (such as .NET Core, .NET 5 and 6) as well. The following code is tested with Windows 7, 64 bit and also with Windows 10, 64 bit.
Instead of using
"Wow6432Node"
, which emulates a node by mapping one registry tree into another making it appear there virtually, you can do the follwing:Decide, whether you need to access the 64 bit or the 32 bit registry, and use it as described below. You may also use the code I mentioned later (Additional information section), which creates a union query to get registry keys from both nodes in one query - so you can still query them by using their real path.
64 bit registry
To access the 64 bit registry, you can use
RegistryView.Registry64
as follows:32 bit registry
If you want to access the 32bit registry, use
RegistryView.Registry32
as follows:Don't be confused, both versions are using
Microsoft.Win32.RegistryHive.LocalMachine
as first parameter, you make the distinction whether to use 64 bit or 32 bit by the 2nd parameter (RegistryView.Registry64
versusRegistryView.Registry32
).Note that
On a 64bit Windows,
HKEY_LOCAL_MACHINE\Software\Wow6432Node
contains values used by 32 bit applications running on the 64 bit system. Only true 64 bit applications store their values inHKEY_LOCAL_MACHINE\Software
directly. The subtreeWow6432Node
is entirely transparent for 32 bit applications, 32 bit applications still seeHKEY_LOCAL_MACHINE\Software
as they expect it (it is a kind of redirection). In older versions of Windows as well as 32 bit Windows 7 (and Vista 32 bit) the subtreeWow6432Node
obviously does not exist.Due to a bug in Windows 7 (64 bit), the 32 bit source code version always returns "Microsoft" regardless which organization you have registered while the 64 bit source code version returns the right organization.
Coming back to the example you've provided, do it the following way to access the 64 bit branch:
Additional information - for practical use:
I'd like to add an interesting approach Johny Skovdal has suggested in the comments, which I've picked up to develop some useful functions by using his approach: In some situations you want to get back all keys regardless whether it is 32 bit or 64 bit. The SQL instance names are such an example. You can use a union query in that case as follows (C#6 or higher):
Now you can simply use the functions above as follows:
Example 1: Get SQL instance names
will give you a list of the value names and values in sqlRegPath.
Note: You can access the default value of a key (displayed by the commandline tool
REGEDT32.EXE
as(Default)
) if you omit theValueName
parameter in the corresponding functions above.To get a list of SubKeys within a registry key, use the function
GetRegKeyNames
orGetAllRegKeyNames
. You can use this list to traverse further keys in the registry.Example 2: Get uninstall information of installed software
will get all 32 bit and 64 bit uninstall keys.
Notice the null handling required in the functions because SQL server can be installed as 32 bit or as 64 bit (Example 1 above). The functions are overloaded so you can still pass the 32 bit or 64 bit parameter if required - however, if you omit it then it will try to read 64 bit, if that fails (null value), it reads the 32 bit values.
There is one speciality here: Because
GetAllRegValueNames
is usually used in a loop context (see Example 1 above), it returns an empty enumerable rather thannull
to simplifyforeach
loops: if it wouldn't be handled that way, the loop would have to be prefixed by anif
statement checking fornull
which would be cumbersome having to do that - so that is dealt with once in the function.Why bothering about null? Because if you don't care, you'll have a lot more headaches finding out why that null reference exception was thrown in your code - you'd spend a lot of time finding out where and why it happened. And if it happened in production you'll be very busy studying log files or event logs (I hope you have logging implemented) ... better avoid null issues where you can in a defensive way. The operators
?.
,?[
...]
and??
can help you a lot (see the code provided above). There is a nice related article discussing the new nullable reference types in C#, which I recommend to read and also this one about the Elvis operator.Hint: You can use the free edition of Linqpad to test all examples under Windows. It doesn't require an installation. Don't forget to press F4 and enter
Microsoft.Win32
in the Namespace import tab. In Visual Studio, you requireusing Microsoft.Win32;
at the top of your code.Tip: To familiarize yourself with the new null handling operators, try out (and debug) the following code in LinqPad:
Example 3: Demonstrating null handling operators
Try it with .Net fiddle
If you're interested, here are some examples I put together showing what else you can do with the tool.