Introduction
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).
Two registry views
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):
- HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Wow6432Node
- HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node
- HKEY_USERS\*\SOFTWARE\Classes\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.
Example
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
.
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#pragma comment(lib, "Advapi32.lib")
int _tmain()
{
LONG lRet;
HKEY hKey;
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.
Shared registry keys
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:
- HKEY_LOCAL_MACHINE\Software\Microsoft\SystemCertificates
- HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography\Services
- HKEY_LOCAL_MACHINE\Software\Classes\HCP
- HKEY_LOCAL_MACHINE\Software\Microsoft\EnterpriseCertificates
- HKEY_LOCAL_MACHINE\Software\Microsoft\MSMQ
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
NT\CurrentVersion\NetworkCards
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
NT\CurrentVersion\ProfileList
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
NT\CurrentVersion\Perflib
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
NT\CurrentVersion\Print
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
NT\CurrentVersion\Ports
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Control
Panel\Cursors\Schemes
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Telephony\Loc
ations
- HKEY_LOCAL_MACHINE\Software\Policies
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Group
Policy
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Setup\OC
Mmanager
- HKEY_LOCAL_MACHINE\Software\Microsoft\Software\Microsoft\Shared
Tools\MSInfo
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Setup
- HKEY_LOCAL_MACHINE\Software\Microsoft\CTF\TIP
- HKEY_LOCAL_MACHINE\Software\Microsoft\CTF\SystemShared
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
NT\CurrentVersion\Fonts
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
NT\CurrentVersion\FontSubstitutes
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
NT\CurrentVersion\FontDpi
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
NT\CurrentVersion\FontMapper
- HKEY_LOCAL_MACHINE\Software\Microsoft\RAS
- HKEY_LOCAL_MACHINE\Software\Microsoft\Driver Signing
- HKEY_LOCAL_MACHINE\Software\Microsoft\Non-Driver
Signing
- HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography\Calais\Current
- HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography\Calais\Readers
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
NT\CurrentVersion\Time Zones
- HKEY_LOCAL_MACHINE\Software\Microsoft\Transaction
Server
- HKEY_LOCAL_MACHINE\Software\Microsoft\DFS
- HKEY_LOCAL_MACHINE\Software\Microsoft\TermServLicensing
Internals
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.
Side effect of shared registry keys
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.
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
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.
Registry reflection
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:
- HKEY_LOCAL_MACHINE\Software\Classes
- HKEY_LOCAL_MACHINE\Software\COM3
- HKEY_LOCAL_MACHINE\Software\EventSystem
- HKEY_LOCAL_MACHINE\Software\Ole
- HKEY_LOCAL_MACHINE\Software\Rpc
- HKEY_USERS\*\Software\Classes
- HKEY_USERS\*_Classes
Note: * indicates a match for all
user security IDs (SID).
Special handling of HKEY_LOCAL_MACHINE\Software\Classes\CLSID
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.
Disable / Enable reflection for particular keys
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
=> Reflection is
enabled! bIsReflectionDisabled
!= FALSE
=> Reflection is
disabled!
Limitations
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.
You should also be aware, that subkeys are not affected by this function! Only
the specified key.
Example
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:
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)!
Internals
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.
What happens if a value is changed
If a value is changed the following will occur:
- Test if Reflection is activated (BOOL ADVAPI32!bReflectorStatusOn)
This
is interesting ... it seems that reflection can be disabled in some ways, or is
not always enabled (maybe at system startup). - Check if the registry key is on the "ReflectionList"
(ADVAPI32!IsOnReflectionListByToken)
This list is fixed coded and stored in
x64-"ADVAPI32!ReflectList". Besides the already listed nodes, it also
contains the following nodes:
- \REGISTRY\MACHINE\SOFTWARE\Wow6432Node\Microsoft\ Windows\CurrentVersion\Run
- \REGISTRY\MACHINE\SOFTWARE\Wow6432Node\Microsoft\
Windows\CurrentVersion\RunOnce
- \REGISTRY\MACHINE\SOFTWARE\Wow6432Node\Microsoft\
Windows\CurrentVersion\RunOnceEx
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...)
- If a value was changed, a "dirty"-flag is set.
- On Closing the hKey, the function
Wow64RegCloseKey
will be called
and will do a NtSyncKey
if the dirty flag is set. The function
Wow64RegCloseKey
is called in x64 apps directly inside the
RegCloseKey
, in x86 apps the function is called indirect through the
CpuSimulation
in WOW64
.
Handle list for dirty keys
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.
Transparent changes in values if written with REG_EXPAND_SZ
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)%
).
#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!
Accessing an alternate registry view (KEY_WOW64_64KEY and
KEY_WOW64_32KEY)
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.
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;
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\MyTest"),
0L, KEY_ALL_ACCESS, &hKey);
if(lRet == ERROR_SUCCESS)
{
#if _M_IX86
lRet = RegDeleteKeyEx(hKey, _T("1"), KEY_WOW64_64KEY, 0);
#else
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.
Side-Effect of KEY_WOW64_64KEY and the special node Wow6432Node
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!).
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.
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.
Changes in Vista
New APIs
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
I had not yet tested this flag;
so I will just quote the original documentation:
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
This flag has the same effect as
using RegDisableReflectionKey
, RegEnableReflectionKey
and RegQueryReflectionKey
KEY_FLAG_OWNERSHIP_REFLECTION
I had not yet tested this
flag; so I will just quote the original documentation:
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.
Internals
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.
Shared registry keys
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)).
Links
History
- 23rd May, 2006
- 07th Oct, 2006
- Added "Transparent changes in values if written with REG_EXPAND_SZ"