Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Registry Redirector in x64 / IA64

4.83/5 (21 votes)
11 Jul 200711 min read 4   772  
This article gives you a deeper view into the registry redirector on x64 / IA64 systems

Registry with Wow6432Node

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.

C++
#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.

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).

C++
#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:

  1. 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).
  2. 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)%).

C++
#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:

C++
#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.

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
    • Initial revision.
  • 07th Oct, 2006
    • Added "Transparent changes in values if written with REG_EXPAND_SZ"

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here