The registry redirector was added in x64/IA64 operating systems to support win32
(x86) applications on the new 64-bit platform. It is designed to smoothly support
"legacy" x86 applications without any modifications on these
applications. Because x86 and x64/IA64 are completely two different worlds, they
decided to make two different registry-views for old (x86) and new (x64/IA64)
applications. But a completely separate registry-view is not always what the app
wants. For example, the registry keys of COM servers (OutProc) should be visible
in both worlds. To address these issues, they indroduced the "Registry
Redirector". This redirector consists of the following three parts:
This article tries to give you and overview and in-deep knowledge of the
registry redirector and the occuring side-effects.
Beside the registry redirector there is also a file system redirector (which is not part of this article).
The location of the 32-bit view of the registry is inside the 64-bit view in a
special sub-node. This node is called Wow6432Node. The following
nodes contain the 32-bit view (and all sub-nodes):
Wow6432Node
The complete registry redirection is done in user-mode. The key-names are
translated in user-mode to the appropriate "kernel-mode" names. So in
general you can say: There is only one registry in windows x64,
but the redirector is sometimes converting the passed named to an other name.
Here is a small example of the registry redirector:If you access the key
HKEY_LOCAL_MACHINE\SOFTWARE\MyApp\Settings from a 64-bit app, you
really opening this node. If you open this node from a 32-bit app, you are
transparently redirected to
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\MyApp\Setting.
HKEY_LOCAL_MACHINE\SOFTWARE\MyApp\Settings
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\MyApp\Setting
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#pragma comment(lib, "Advapi32.lib")
int _tmain()
{
LONG lRet;
HKEY hKey;
// Open the key in the default-view
lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
_T("SOFTWARE\\MyApp\\MySettings"),
0, NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, NULL);
if(lRet == ERROR_SUCCESS)
{
#if _M_IX86
TCHAR szValue[] = _T("x86");
RegSetValueEx(hKey, _T("AppType"), 0, REG_SZ,
(const BYTE*) szValue,
(DWORD) (_tcslen(szValue)+sizeof(TCHAR))*sizeof(TCHAR));
#else
TCHAR szValue[] = _T("x64 / IA64");
RegSetValueEx(hKey, _T("AppType"), 0, REG_SZ,
(const BYTE*) szValue,
(DWORD) (_tcslen(szValue)+sizeof(TCHAR))*sizeof(TCHAR));
#endif
RegCloseKey(hKey);
}
return 0;
}
After executing this example as 32-bit 'and' 64-bit application, you have
created two separate registry keys. One containing the "x86" value and
the other containing the "x64 / IA64" value.
There are several hard coded registry keys which will be shared by x86 and
x64/IA64 apps. These keys exists only in the 64-bit view of the registry but you
can open it in an x86 app. The list of these keys is available via KB article 896459 and here (by the way, the KB article has one misspelled
entry). Just for completeness, here is the current list:
This list can also be found in ADVAPI32!ExemptRedirectedKey by using windbg.
Every time a key is opened in an x86 app (this is done via
Wow64RegOpenKeyEx and Wow64pRegCreateKeyEx), it is
checked in ADVAPI32!IsExemptRedirectedKey if the key should be open
in the default-view or in the x64 view.
Wow64RegOpenKeyEx
Wow64pRegCreateKeyEx
ADVAPI32!IsExemptRedirectedKey
Because the keys exist only in the 64-bit view of the registry, there is
currently no way to find these keys in an x86 app via enumerating
(RegEnumKeyEx). You can simply verify this by opening the 32-bit
version of regedit.exe via %systemroot%\SysWOW64\regedit.exe
-m (-m to allow multiple instances of regedit.exe) and try to find these
keys.
RegEnumKeyEx
%systemroot%\SysWOW64\regedit.exe
-m
See also my blog entry about
this behavior.
You might think that you can enumerate the whole 64-bit registry from an x86
app by using the KEY_WOW64_64KEY flag. But this is also not possible
due to other "features", see
KEY_WOW64_64KEY
Because these keys are handled differently (via
IsExemptRedirectedKey), it makes no sense to open these keys with
KEY_WOW64_64KEY or KEY_WOW64_32KEY. The flags are simply
ignored.
IsExemptRedirectedKey
KEY_WOW64_32KEY
Registry reflection is a really special part of the registry redirector. Like
shared registry keys, there is also a hard-coded list of keys which will be
reflected. Reflection means that two physical copies of a single key exist (in x86
view and x64/IA64 view), but these keys will be "reflected" if their
contents are changed and the key is closed. Because the reflection only occurs
with the closing of the key, the last close wins. Here is a list of relfected
keys:
This node contains all the COM components. Activating a COM component can be
done in two different ways: Either in-process or out-process. This is specified in
the registry under the CLSID of the component. There is either a sub-entry
InProcServer32 or LocalServer32. The registry reflector
is now 'intelligent' and only reflects keys which are bound to an out-process
("LocalServer32") server. It only makes sense for out-process to relfect
the keys, because in-proc COM-servers can either be 32-bit or
64-bit. There is no way to load a 64-bit DLL into a 32-bit process and vice versa.
So if you register an in-proc component, it is bound to the architecture of the
DLL (either 32-bit or 64-bit). So the affected registry keys must not be reflected
in the other world.
InProcServer32
LocalServer32
By default, reflection is enabled for all (sub-keys) of the hard-coded
reflection list. With the new APIs RegDisableReflection
Key and RegEnableReflectionK
ey it is possible to change the reflection state of these particular keys. Of
course, the current state of the key can be queried using the RegQueryReflectionKe
y function. But you should be aware that the out-parameter is named
bIsReflectionDisabled! This means, you must think outside the box.
For the sake of simplicity I have a list for you:
bIsReflectionDisabled
FALSE
It appears that this function can be used to enable reflection for any
particular key. But this is not true! By default, all keys will get reflected
which are on the hard-coded reflection list! You cannot add keys to this
hard-coded list! The only thing you can do, is disable reflection for a key which
would normally be reflected, because it is on the hard-coded reflection list.
The function RegDisableReflectionKey just sets a special
Tag to the specified key. The RegEnableReflectionKey
removes this Tag. So the functions will succeed on 'all' keys, but it
only will affect keys which are on the hard-coded reflection list.
RegDisableReflectionKey
Tag
RegEnableReflectionKey
You should also be aware, that subkeys are not affected by this function! Only
the specified key.
In the follwoing example, I disable reflection for a key, which will never get
reflected (just to show that the function will succeed, even it has no affect,
because the key will never get reflected).
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#pragma comment(lib, "Advapi32.lib")
void ShowReflectionState(HKEY hKey)
{
BOOL bReflectedDisabled = TRUE;
LONG lResult = RegQueryReflectionKey(hKey, &bReflectedDisabled);
if (lResult == ERROR_SUCCESS)
{
if (bReflectedDisabled != FALSE)
_tprintf(_T("DISABLED!\n"));
else
_tprintf(_T("ENABLED (means: Tag is not set)!\n"));
}
else
_tprintf(_T("Error (RegQueryReflectionKey): 0x%8.8x\n"), lResult);
}
int _tmain()
{
HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\MyApp"),
0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
{
_tprintf(_T("Current reflection state: "));
ShowReflectionState(hKey);
_tprintf(_T("Disabling reflection: "));
LONG lRes = RegDisableReflectionKey(hKey);
if (lRes != ERROR_SUCCESS)
_tprintf(_T("Error: 0x%8.8x\n"), lRes);
else
_tprintf(_T("Ok\n"));
_tprintf(_T("New reflection state: "));
ShowReflectionState(hKey);
_tprintf(_T("Enabling reflection: "));
lRes = RegEnableReflectionKey(hKey);
if (lRes != ERROR_SUCCESS)
_tprintf(_T("Error: 0x%8.8x\n"), lRes);
else
_tprintf(_T("Ok\n"));
_tprintf(_T("New reflection state: "));
ShowReflectionState(hKey);
RegCloseKey(hKey);
}
return 0;
}
With this example you will get the following output, if you created a new
MyApp-node:
MyApp
Current reflection state: ENABLED (means: Tag is not set)!
Disabling reflection: Ok
New reflection state: DISABLED!
Enabling reflection: Ok
New reflection state: ENABLED (means: Tag is not set)!
In general, Reflection is only done in x64/IA64 mode. So all functions are in
the x64 version of the ADVAPI32.DLL. Reflection is only done in
user-mode. No kernel-mode component needs to know about reflection, because every
kernel-mode component is x64/IA64 and threfore no reflection is needed.
If a value is changed the following will occur:
But I really cannot see that these keys are getting reflected ... maybe someone
can get it to work (it seems that the corresponding key without the
Wow6432Node is missing...)
Wow64RegCloseKey
NtSyncKey
RegCloseKey
CpuSimulation
WOW64
The handle-list for open registry keys which contains the "dirty"
flags is restricted to 512? entries. So if you open more than 512 registry keys
(and keep them open) and all keys must be reflected, the latest keys will not be
reflected because of this restriction. Of course, you should never keep 512
registry keys open at the same time, so this should be no real-world restriction.
Writing REG_EXPAND_SZ strings from an x86 application (running under WOW64)
into the registry is something special. Because with REG_EXPAND_SZ, you can embed
environment variables into the string. And there might be a change where, some x64
application might read this string. Therefore, MS decided to change the value of
such strings "on-the-fly" if it contains environment variables which
might lead to the wrong directory. These environment varaibles are:
%ProgramFiles%
%commonprogramfiles%
If you write such a string then it will be replaced with the according x86
value (%ProgramFiles(x86)%,
%commonprogramfiles(x86)%).
%ProgramFiles(x86)%
%commonprogramfiles(x86)%
#include <windows.h>
#include <tchar.h>
int _tmain()
{
HKEY hKey;
RegCreateKeyEx(HKEY_CURRENT_USER, _T("SOFTWARE\\TEST"), 0, NULL, 0,
KEY_ALL_ACCESS, NULL, &hKey, NULL);
LPCTSTR szTest1 = _T("%ProgramFiles%\\Test");
RegSetValueEx(hKey, _T("Test1"), 0, REG_EXPAND_SZ, (LPBYTE) szTest1,
(DWORD) (_tcslen(szTest1)+1)*sizeof(TCHAR));
RegCloseKey(hKey);
return 0;
}
But it seems that all x64 OSs are having a bug regarding this feature. Because
they currently compare this strings case-sensitive!
There a new flags which can be used to access the alternate registry view in
your 32-bit or 64-bit application. With this flag, you can force
RegCreateKeyEx, RegDeleteKeyEx and
RegOpenKeyEx to explicitly access the 64-bit view
(KEY_WOW64_64KEY) or the 32-bit (KEY_WOW64_32KEY) view
of the registry, independent of your process' default view. Of course, this only
makes sense if you access some keys which will be redirected.
RegCreateKeyEx
RegDeleteKeyEx
RegOpenKeyEx
This flag is very special and is not associated with the returned handle!
If you want to delete a key and you have opened the node with
KEY_WOW64_64KEY, then you expect that the RegDeleteKeyEx
function will be performed on the 64-bit view. But this is not the case! It will
be performed on the default process view! If you want to delete a 64-bit key from
a 32-bit app, you need to explicitly pass the KEY_WOW64_64KEY flag to
the RegDeleteKeyEx. You do not need to open the node with
KEY_WOW64_64KEY (if the node exists in both views).
Here is a short example:
#include <windows.h>
#include <tchar.h>
#pragma comment(lib, "Advapi32.lib")
void _tmain()
{
LONG lRet;
HKEY hKey;
// Open the key on the default-view
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\MyTest"),
0L, KEY_ALL_ACCESS, &hKey);
if(lRet == ERROR_SUCCESS)
{
// delete it on the other view...
#if _M_IX86
// The key will be deleted on the 64-bit view, even if the app is 32-bit
// and the key was opend on the 32-bit view!
lRet = RegDeleteKeyEx(hKey, _T("1"), KEY_WOW64_64KEY, 0);
#else
// The key will be deleted on the 32-bit view, even if the app is 64-bit
// and the key was opend on the 64-bit view!
lRet = RegDeleteKeyEx(hKey, _T("1"), KEY_WOW64_32KEY, 0);
#endif
RegCloseKey(hKey);
}
}
The other way is also possible: You can open the key with
KEY_WOW64_xxKEY and if you do not specify this flag on
RegDeleteKeyEx, the key will be deleted on the default-view.
KEY_WOW64_xxKEY
You should not open the following keys with KEY_WOW64_64KEY
because they will return the node of the key without the Wow6432Node.
I do not know if this is by design or a bug ...
SOFTWARE\Classes\Wow6432Node
SOFTWARE\Wow6432Node
So if you try to enumerate throw the registry with KEY_WOW64_64KEY
specified, you will end up in an endless loop!
Also if you open a subkey of the Wow6432Node such as
SOFTWARE\Wow6432Node\SubKey in an x86 app, the
Wow6432Node part is ignored. So if you specify
KEY_WOW64_64KEY, the x64-node SOFTWARE\SubKey is opened.
If you use the default view, also the node SOFTWARE\SubKey is opened
(in the accoring view!).
SOFTWARE\Wow6432Node\SubKey
SOFTWARE\SubKey
Also you should not create own sub-nodes with the name Wow6432Node, because this can lead to confusing when opening a key. So for example: opening SOFTWARE\Wow6432Node\SubKey\Wow6432Node will open the node SOFTWARE either in the default view (if no special flag is passed) or in the x64-view if KEY_WOW64_64KEY was passed.
SOFTWARE\Wow6432Node\SubKey\Wow6432Node
SOFTWARE
In x86 apps it seems that the node Wow6432Node is just removed
from the path and the accoring to the flags either the x64 or the x86
registry-view is used.In x64 apps the node Wow6432Node is
removed if you specify the KEY_WOW64_64KEY flag.
In x64 apps, if you try to open a key with KEY_WOW64_32KEY and the
path contains a node named Wow6432Node, it always returns the
root-node! At least this is a bug I think.
In Windows
Vista they introduced a new API function for changing the behavior of the
registry redirector for special keys. The functions are called RegSetKeyFlags and
RegQueryKeyFlags. It has three possible flags which can be set or cleared:
KEY_FLAG_DISABLE_REDIRECTION
Disables registry redirection for operations that use this handle. (By
default, WOW64 redirects operations on handles opened in the 64-bit registry view
to the 32-bit registry view.)
KEY_FLAG_EXEMPT_REFLECTION
RegQueryReflectionKey
KEY_FLAG_OWNERSHIP_REFLECTION
Exempts the key from reflection if the key exists in an alternate
registry view and was not created by the caller.
It adds more control over redirection and reflection, but it also adds a more
complex mechanism to determine what exactly happens if these flags are set.
The new APIs use the same mechanism to store the settings as the current
available APIs (RegDisableReflectionKey,
RegEnableReflectionKey). The setting is stored using a key-tag (via
ADVAPI32!QueryKeyTag and ADVAPI32!UpdateKeyTag). So
using RegDisableReflectionKey or RegSetKeyFlags with
EY_FLAG_EXEMPT_REFLECTION has the same effect. The setting is stored
in the same tag, so you can also use the RegQueryKeyFlags or
RegQueryReflectionKey to query the reflection state.
ADVAPI32!QueryKeyTag
ADVAPI32!UpdateKeyTag
RegSetKeyFlags
EY_FLAG_EXEMPT_REFLECTION
RegQueryKeyFlags
It seems that the build 5365 has a bug in the
ADVAPI32!ExemptRedirectedKey list of shared keys. The key
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Time
Zones is misspelled and is now missing a "s" at the end (the same
confusion is in the two documentations about the list of shared keys (see above)).
ADVAPI32!ExemptRedirectedKey
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Time.