Registry 32-bit/64-bit Redirection and Virtualization Pitfalls — Wow6432Node and the "The Value I Wrote Isn't There" Problem

· · Registry, WOW64, Wow6432Node, UAC, 64-bit Migration, Windows, C#, Troubleshooting

“The application can’t read the licence key the installer wrote at start-up. Yet the value is definitely there when you look in regedit.” — That was the report we received while supporting the 64-bit migration of an equipment-integration application. On investigation, the value was under HKLM\Software\Wow6432Node\<company name>. The application, freshly rebuilt as 64-bit, was looking at HKLM\Software\<company name>, where there was nothing at all.

This kind of “the value I wrote isn’t there” or “regedit can see it but the app can’t” remains a mystery no matter how much you inspect by eye, unless you know two mechanisms: that on 64-bit Windows the registry appears in a different place depending on the process bitness (WOW64 registry redirection), and that a write without sufficient privileges is silently diverted elsewhere (UAC registry virtualization). Worse, both “succeed without an error”, so the problem only shows up the moment the place written to and the place read from diverge — in other words, at the point of a 64-bit migration or an installer change.

This article is aimed at developers of Windows business applications. It sets out how WOW64 registry redirection works, which keys are redirected and which are shared, the conditions that trigger UAC registry virtualization, the real damage that surfaces in installers and COM registration, and the correct way to make the view explicit in C#/C++/reg.exe — all backed by the official documentation.

1. The Bottom Line First

  • The registry on 64-bit Windows has a 64-bit view and a 32-bit view, and a 32-bit process’s access to HKLM\Software is transparently steered by the registry redirector to the physical location HKLM\Software\Wow6432Node. The application sees neither an error nor a warning.1
  • The physical location Wow6432Node is reserved by the system. You must not hard-code the path and access it directly (on Windows 10 on ARM, a different location, WowAA32Node, is used for 32-bit ARM). Access the other view through the official mechanisms (the flags and RegistryView described below).12
  • Only some keys are redirected; others are shared. On Windows 7 and later, HKLM\SOFTWARE is “redirected”, HKLM\SOFTWARE\Classes is “shared”, but CLSID and Interface beneath it are “redirected” again — a nested structure.3
  • To access the other view: in Win32, specify KEY_WOW64_64KEY / KEY_WOW64_32KEY in the samDesired argument of RegOpenKeyEx and friends; in .NET, pass RegistryView.Registry64 / Registry32 to RegistryKey.OpenBaseKey; on the command line, use reg.exe with /reg:64 / /reg:32.245
  • When a 32-bit interactive process without administrator privileges writes to HKLM\Software, UAC registry virtualization may divert the write to HKEY_USERS\<SID>_Classes\VirtualStore\Machine\Software. The trap is that the write “succeeds” and, when the same process reads it back, the merged view makes it visible.6
  • Specifying requestedExecutionLevel in the manifest disables file and registry virtualization. Put the other way round, only legacy EXEs without a manifest are subject to virtualization.7
  • Registry virtualization is an interim compatibility technology, and Microsoft states explicitly that it intends to remove it from a future version of Windows. A new application must not depend on it. As a design principle, “don’t write to HKLM” is the right answer.6
  • COM CLSID registration (HKCR\CLSID = HKLM\Software\Classes\CLSID) is split by bitness. A 32-bit COM server’s registration is invisible to a 64-bit client — the standard cause of “Class not registered (0x80040154)”.3

2. WOW64 Registry Redirection — Where Is a 32-bit Process Actually Looking?

64-bit Windows runs 32-bit applications on a subsystem called WOW64. In doing so, the registry redirector presents separate logical views to 32-bit and 64-bit processes. The heart of the mechanism is that both specify the same key name through the same API (HKEY_LOCAL_MACHINE\Software\...), yet the physical location actually read from and written to differs.1

The physical location of a redirected key is Wow6432Node. For example, HKEY_LOCAL_MACHINE\Software for a 32-bit process is physically mapped to HKEY_LOCAL_MACHINE\Software\Wow6432Node. This mapping is entirely transparent to the application, so a 32-bit app can manipulate the registry “as though it were running on 32-bit Windows”.1

Tabulating who looks where gives the following.

Caller Path specified in code Physical location actually read/written
64-bit process HKLM\Software\MyApp HKLM\Software\MyApp
32-bit process HKLM\Software\MyApp HKLM\Software\Wow6432Node\MyApp
32-bit process + KEY_WOW64_64KEY (RegistryView.Registry64) HKLM\Software\MyApp HKLM\Software\MyApp
64-bit process + KEY_WOW64_32KEY (RegistryView.Registry32) HKLM\Software\MyApp HKLM\Software\Wow6432Node\MyApp
Write by a 32-bit interactive process, standard privileges, no manifest HKLM\Software\MyApp HKCU\Software\Classes\VirtualStore\Machine\Software\Wow6432Node\MyApp (virtualized after WOW64 redirection; see Section 5)
regedit (a 64-bit process) Displayed relative to the 64-bit view. Wow6432Node is also visible as an ordinary physical key

The war story at the top of this article is that table exactly. The value written by the 32-bit installer is physically under Wow6432Node, while the now-64-bit application reads plain HKLM\Software. Regedit sees both, so the site report “it’s there in regedit” and the symptom “the app can’t see it” are perfectly consistent with each other.

You may now be tempted to hard-code the Wow6432Node path to square the circle, but that is an anti-pattern the official documentation clearly prohibits. The physical location of the redirection target is reserved by the system and is subject to change. Indeed, on Windows 10 on ARM the redirection target for 32-bit ARM apps is a different key, WowAA32Node.12 If you want to read the other view, use the official mechanisms in Section 4.

As a finer detail, WOW64 also performs a correction that replaces REG_SZ/REG_EXPAND_SZ strings beginning with %ProgramFiles% written by a 32-bit app with %ProgramFiles(x86)% (only when the case matches exactly).1 In addition, the Vista/XP era had “registry reflection”, which copied and synchronised keys between the 32-bit and 64-bit views, but this was removed in Windows 7 / Windows Server 2008 R2. When reading older explanatory articles, watch out for this difference in premise.1

3. Keys That Are Redirected and Keys That Are Shared

Not every key is redirected. Some keys share a single physical copy between both views. Here is an extract from the list in the official document “Registry Keys Affected by WOW64”, covering the entries most relevant to business application development (the column for Windows 7 / Server 2008 R2 and later; subkeys generally inherit the behaviour of their parent).3

Key Behaviour on Windows 7 and later
HKLM\SOFTWARE Redirected
HKLM\SOFTWARE\Classes Shared
HKLM\SOFTWARE\Classes\CLSID Redirected
HKLM\SOFTWARE\Classes\Interface Redirected
HKLM\SOFTWARE\Classes\DirectShow / Media Type / MediaFoundation Redirected
HKLM\SOFTWARE\Clients Shared
HKLM\SOFTWARE\Microsoft\COM3 / EventSystem / OLE / RPC Shared
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths Shared
HKLM\SOFTWARE\Policies Shared
HKCU\SOFTWARE Shared
HKCU\SOFTWARE\Classes Shared
HKCU\SOFTWARE\Classes\CLSID / Interface Redirected

What stands out is the nesting. HKLM\SOFTWARE is redirected, yet Classes beneath it reverts to shared, and CLSID and Interface below that are redirected once more. With the sloppy understanding that “everything under Software goes to Wow6432Node”, you cannot explain the difference in behaviour between file extension associations (directly under Classes, shared) and COM class registration (Classes\CLSID, redirected). HKCU is basically shared, which yields another important practical consequence: as long as you keep per-user settings in HKCU, bitness problems barely arise at all.3

Note also that flags such as KEY_WOW64_64KEY have no effect on shared keys. Since there is only one copy of a shared key to begin with, there is no meaning in switching views.2

4. Reading the Other View Explicitly — The Correct Way in reg.exe, C#, and C++

The fastest way to establish “which view holds the value” is the /reg:64 / /reg:32 options of reg.exe. They access the 64-bit view and the 32-bit view respectively, explicitly.5

:: Read the 64-bit view (plain HKLM\Software)
reg query "HKLM\SOFTWARE\KomuraSoft\DeviceLink" /v LicenseKey /reg:64

:: Read the 32-bit view (physically under Wow6432Node)
reg query "HKLM\SOFTWARE\KomuraSoft\DeviceLink" /v LicenseKey /reg:32

Run these two lines, and if the value exists in only one of them, you have confirmed that “the bitness of the writer and the reader don’t match”. The key point is to switch only the view against the same logical path, rather than hand-writing Wow6432Node into the path.

In C# (.NET), you pass a RegistryView to RegistryKey.OpenBaseKey. There are three values — Registry64 (256), Registry32 (512), and Default (0) — and they can be specified in OpenBaseKey / OpenRemoteBaseKey / FromHandle.4

using Microsoft.Win32;

// Read the 64-bit view even from a 32-bit process
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
using (var key = baseKey.OpenSubKey(@"SOFTWARE\KomuraSoft\DeviceLink"))
{
    var license = key?.GetValue("LicenseKey") as string;
}

// Read the 32-bit view (the Wow6432Node side) from a 64-bit process
// — used, for example, to read across values written by a 32-bit-era installer
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32))
using (var key = baseKey.OpenSubKey(@"SOFTWARE\KomuraSoft\DeviceLink"))
{
    var legacy = key?.GetValue("LicenseKey") as string;
}

RegistryView.Default leaves it up to the process bitness. Because an AnyCPU .NET application can be 32-bit or 64-bit depending on the runtime environment, stating in code which view you read when handling machine-wide data under HKLM prevents the incident where a change in build settings (toggling Prefer 32-bit, or a 64-bit migration) suddenly changes how the registry looks. Note too that requesting Registry64 on a 32-bit OS returns keys from the 32-bit view by design, so the same code works safely even where 32-bit OS support remains.4

In C++ (the Win32 API), you OR KEY_WOW64_64KEY (0x0100) or KEY_WOW64_32KEY (0x0200) into the samDesired argument of RegOpenKeyEx / RegCreateKeyEx / RegDeleteKeyEx. Specifying both at once fails with ERROR_INVALID_PARAMETER.2

HKEY hKey = nullptr;
// Open a key in the 64-bit view from a 32-bit process
LSTATUS st = RegOpenKeyExW(
    HKEY_LOCAL_MACHINE,
    L"SOFTWARE\\KomuraSoft\\DeviceLink",
    0,
    KEY_READ | KEY_WOW64_64KEY,   // the view is made explicit here
    &hKey);
if (st == ERROR_SUCCESS)
{
    wchar_t buf[256]; DWORD cb = sizeof(buf); DWORD type = 0;
    RegQueryValueExW(hKey, L"LicenseKey", nullptr, &type,
                     reinterpret_cast<LPBYTE>(buf), &cb);
    RegCloseKey(hKey);
}

The official documentation raises two caveats. Once you have opened the other view with a flag, keep specifying the same flag for operations on the child keys beneath it (create, delete, open). Mixing them produces unexpected behaviour. Also, if you want to enumerate the keys of both views exhaustively, you need to enumerate in two passes — one with a handle opened using KEY_WOW64_64KEY and one with KEY_WOW64_32KEY. Note as well that RegDeleteKey (without the Ex) cannot access the other view.2

5. UAC Registry Virtualization — I Wrote to HKLM, So Why Is It in the VirtualStore?

The other mechanism, easily confused with redirection, is UAC registry virtualization. This is not about bitness but about privileges: since Vista, it has been a compatibility technology for rescuing legacy applications written on the assumption of administrator privileges.6

Here is how it behaves. When a process without write permission attempts to write a value or create a subkey under HKLM\Software, instead of failing with access denied, the write is diverted to a per-user virtual store, HKEY_USERS\<user SID>_Classes\VirtualStore\Machine\Software (visible in regedit under HKCU\Software\Classes\VirtualStore\Machine\Software). Furthermore, on read, a merged view of the virtual store’s values and the values in the original global store is returned, with the virtual store taking precedence for identically named values.6 Note that for a 32-bit process on a 64-bit OS, the WOW64 redirection from Section 3 applies first, so the actual destination of a write to HKLM\Software\MyApp becomes VirtualStore\Machine\Software\Wow6432Node\MyApp. When investigating under VirtualStore, always check the Wow6432Node side as well as the plain Software side.

In other words, the process that did the writing can read and write as though nothing had happened. This is the real identity of those user-dependent oddities: “it’s fine on the development machine (run as administrator), but the settings only go wrong in the customer’s standard-user environment”, or “it works when user A logs on, but reverts to defaults for user B”. The virtual store is part of the user profile (NTUSER.DAT and so on), so its contents differ per user (for the structure of the profile, see “Introduction to Windows User Profiles - AppData and NTUSER.DAT”).

The conditions under which virtualization applies are narrow, and they are stated explicitly in the official documentation.6

  • It applies only to operations by a 32-bit interactive process on keys under HKLM\Software that an administrator would be able to write to
  • The following are excluded: 64-bit processes, non-interactive processes such as services, operations while impersonating a user, drivers, and processes that specify requestedExecutionLevel in the manifest
  • Everything under HKLM\Software\Classes, HKLM\Software\Microsoft\Windows, and HKLM\Software\Microsoft\Windows NT is also excluded

The point that bites hardest in practice is that “behaviour changes depending on whether a manifest is present”. Visual Studio’s C++ linker embeds an asInvoker UAC fragment into the manifest by default,7 so an EXE built with a modern toolchain is exempt from virtualization from the outset. Where you meet virtualization is on sites running legacy EXEs without a manifest — built with VB6, old Delphi, or old VC++ — on 64-bit Windows. Conversely, there is a migration trap in the other direction: the moment you “just add a manifest” to a legacy EXE, or rebuild it as 64-bit, virtualization switches off and the writes now genuinely fail with access denied (or silently fail to write). The three values of requestedExecutionLevel (asInvoker / highestAvailable / requireAdministrator) and the thinking behind privilege design are covered in detail in “When Do You Actually Need Administrator Privileges on Windows? - UAC, Protected Areas, and How to Tell by Design”.

What is worth emphasising is that the official documentation states explicitly that virtualization is an interim compatibility technology and that Microsoft intends to remove it from a future version of Windows. Depending on this behaviour in new development is out of the question, and the design principle is: “an application does not write to sensitive system areas (HKLM). Data belongs either in a per-user location or in a common location with appropriate ACLs.”6 Deciding what to store where is organised in “How to Choose Where a Windows App Stores Local Data — A Decision Table for SQLite / JSON / Registry / Access”.

There are also flags for controlling virtualization on a per-key basis (REG_KEY_DONT_VIRTUALIZE / REG_KEY_DONT_SILENT_FAIL / REG_KEY_RECURSE_FLAG), which can be queried and set with reg.exe, as in reg flags HKLM\Software\AppKey1 QUERY.6 When investigating a problem, two quick checks are: display the “UAC virtualization” column on the Details tab of Task Manager to see a process’s virtualization status, and look under HKCU\Software\Classes\VirtualStore for diverted leftovers.

6. The Real Damage in Installers and COM Registration

The places where these two mechanisms most readily erupt into real damage are installers and COM registration.

The installer bitness problem. Settings written by a 32-bit installer (a 32-bit MSI or a 32-bit setup EXE) to HKLM\Software\<company name> land physically under Wow6432Node. If you make the application itself 64-bit but reuse the 32-bit installer as-is, you get exactly the war story from the top of this article: “the installer wrote it, the application can’t read it”. The reverse pattern (a 64-bit installer plus a 32-bit application) is the same. The remedies are to align the installer and the application on “which view is written to and which view is read”, and, during the transition to 64-bit, to implement migration reads from the old location using RegistryView.Registry32 from Section 4.

The COM registration bitness problem. As the table in Section 3 shows, HKLM\Software\Classes\CLSID (the HKLM side of HKCR\CLSID) is subject to redirection. That means a 32-bit COM server’s CLSID registration goes into the 32-bit view and a 64-bit registration into the 64-bit view, and neither can see the other.3 A 64-bit process could not load a 32-bit DLL in-process COM server anyway, so the separation itself is reasonable — but in the field it hits you in the form of “I registered it with regsvr32, yet the client gets 0x80040154 (Class not registered)”. regsvr32 itself also exists in a 32-bit version (on the SysWOW64 side) and a 64-bit version (on the System32 side), and which one you used determines which view gets written. The full picture of the traps arising from the combination of COM registration and bitness is set out in “Registration and Bitness Pitfalls in COM/OCX/ActiveX Development”, and the option of doing away with registry registration altogether is covered in “What Is Reg-Free COM - Using COM Without Registration”.

The COM world carries one more piece of historical baggage. In the Vista/XP era, CLSID and similar keys were handled with “redirection plus reflection (synchronisation between the two views)”, but reflection was removed in Windows 7 and today it is purely a per-view separation.3 There are also a number of compatibility symbolic links defined, such as HKLM\SOFTWARE\Wow6432Node\ClassesHKLM\SOFTWARE\Classes\Wow6432Node, but these exist to rescue existing applications that hard-coded Wow6432Node and are not something new applications should use.3

Note also that even once you have got past the registry separation, a separate set of name resolution rules awaits when it comes to loading the DLL itself. For “can’t be found” investigations, “How Windows DLL Name Resolution Works - Search Order and SxS” is a useful companion.

7. Troubleshooting — Use Procmon to See Where It Actually Read From

Even if you know how the mechanisms work, pinning down “which physical key did this process actually read?” for the failure in front of you requires observation. The most powerful tool here is Sysinternals Process Monitor (Procmon).

The procedure is simple.

  1. Launch Procmon and add the filter Process Name is <target app>.exe
  2. Use the toolbar to show registry operations only (a filter of Operation begins with Reg also works)
  3. Reproduce the problematic operation in the application and look at the RegOpenKey / RegQueryValue / RegSetValue rows

The key point is that Procmon’s Path column shows the physical path after redirection has been resolved. Even if the 32-bit app thinks it opened HKLM\Software\MyApp, Procmon shows HKLM\SOFTWARE\WOW6432Node\MyApp. If NAME NOT FOUND entries are lined up there, “which key is missing in which view” is obvious at a glance; and if a write ends up in HKCU\Software\Classes\VirtualStore\..., you can observe virtualization kicking in. For a COM 0x80040154 investigation, you can even trace which view the failed open of CLSID\{...} occurred in. For the details of designing Procmon filters and reading its output, see “A Practical Guide to Process Monitor (ProcMon)”.

8. Practical Rules of Thumb (Decision Table)

Situation What to do Rationale / notes
“regedit can see it but the app can’t” Compare both views with reg query ... /reg:64 and /reg:32 Establish which view holds the value first5
Both 32-bit and 64-bit in-house processes read the same HKLM setting Fix the view on the writing side (e.g. the 64-bit view) and have every reader specify the same view explicitly Standardise on RegistryView.Registry64 / KEY_WOW64_64KEY42
Tempted to hard-code Wow6432Node in the code Don’t. Replace it with the view-specifying APIs The physical location is reserved by the system. On ARM it becomes WowAA32Node12
Where to put per-user settings Put them in HKCU (or AppData) HKCU is a shared key, so there are no bitness problems and no privilege problems3
A legacy 32-bit app’s settings “differ from user to user” Check HKCU\Software\Classes\VirtualStore The classic pattern of values diverted by virtualization accumulating per user6
Adding a manifest to a legacy EXE / making it 64-bit Inventory the HKLM write sites before you do it Virtualization is disabled, and writes that “worked” until now start failing67
COM 0x80040154 Check the bitness of client and server, and verify registration with the matching regsvr32/view CLSID registration is separated per view3
Can’t establish where it read from Observe the physical path and result (NAME NOT FOUND and so on) with Procmon Stop guessing and look at the facts — it’s the shortest route

9. Summary

  • The registry on 64-bit Windows has two views, and a 32-bit process’s HKLM\Software is transparently redirected to Wow6432Node. It is the prime suspect for “the value I wrote isn’t there”.
  • Redirection does not apply to every key. Get the nested structure straight: Classes is shared, CLSID / Interface beneath it are redirected, and HKCU is almost entirely shared.
  • Never hard-code Wow6432Node. Make the view explicit with reg.exe’s /reg:64 /reg:32, .NET’s RegistryView, or Win32’s KEY_WOW64_64KEY / KEY_WOW64_32KEY.
  • UAC registry virtualization silently diverts under-privileged HKLM writes by manifest-less 32-bit interactive processes to the VirtualStore. It is an interim technology for rescuing legacy apps, and a new application must not depend on it.
  • Align “which view is used” between the installer and the application, and between the COM server and the client. When migrating to 64-bit, design in a migration read from the old view.
  • When in doubt, observe the physical path with Procmon. Observation is faster and more reliable than guesswork.

KomuraSoft LLC handles investigation of faults such as “the value I wrote to the registry can’t be read” and “the COM registration can’t be found”, 64-bit migration design for 32-bit application and COM component assets, and bespoke development of Windows business applications including equipment-integration software.

References

  1. Microsoft Learn, Registry Redirector. On the registry redirector presenting separate logical views to 32-bit and 64-bit applications transparently; HKEY_LOCAL_MACHINE\Software being redirected to HKEY_LOCAL_MACHINE\Software\Wow6432Node; the physical location being reserved by the system and not to be accessed directly by applications; 32-bit ARM keys on Windows 10 on ARM being mapped to WowAA32Node; the replacement of %ProgramFiles% strings; and reflection having been removed in Windows 7 / Windows Server 2008 R2.  2 3 4 5 6 7 8

  2. Microsoft Learn, Accessing an Alternate Registry View. On the meaning of KEY_WOW64_64KEY (0x0100) and KEY_WOW64_32KEY (0x0200); specifying them in the samDesired argument of RegCreateKeyEx, RegDeleteKeyEx, and RegOpenKeyEx; specifying both flags at once resulting in ERROR_INVALID_PARAMETER; the flags having no effect on shared keys; continuing to use the same flag for child key operations; enumerating all keys in two passes; and Wow6432Node/WowAA32Node being reserved keys.  2 3 4 5 6 7 8

  3. Microsoft Learn, Registry Keys Affected by WOW64. On the list of redirected and shared keys (on Windows 7 and later, HKLM\SOFTWARE is redirected, HKLM\SOFTWARE\Classes is shared, Classes\CLSID, Interface, DirectShow and others are redirected, while Clients, COM3, OLE, RPC, App Paths, Policies, HKCU\SOFTWARE and others are shared); subkeys inheriting the behaviour of their parent; HKCR being a merged view of the HKLM and HKCU Classes keys; and the compatibility symbolic links involving Wow6432Node being there to rescue existing applications and not for new applications to use.  2 3 4 5 6 7 8 9

  4. Microsoft Learn, RegistryView Enum (Microsoft.Win32). On the RegistryView enumeration’s Default (0), Registry64 (256), and Registry32 (512) members; the view being specifiable in OpenBaseKey, OpenRemoteBaseKey, and FromHandle; and a request for the 64-bit view on a 32-bit OS returning keys from the 32-bit view.  2 3 4

  5. Microsoft Learn, reg query. On the reg query command’s /reg:32 option accessing the key in the 32-bit registry view and /reg:64 accessing it in the 64-bit registry view.  2 3

  6. Microsoft Learn, Registry Virtualization. On writes to HKLM\Software being redirected to HKEY_USERS<User SID>_Classes\VirtualStore\Machine\Software; reads returning a merged view in which the virtual store takes precedence; virtualization applying only to 32-bit interactive processes, only under HKLM\Software, and only to keys an administrator can write to; it being disabled for 64-bit processes, services, operations while impersonating, processes specifying requestedExecutionLevel, and subkeys such as Classes; it being an interim compatibility technology intended for removal in future that applications should not depend on; and the control of REG_KEY_DONT_VIRTUALIZE and similar flags via reg flags.  2 3 4 5 6 7 8 9

  7. Microsoft Learn, Application manifests. On the meaning of the requestedExecutionLevel element’s asInvoker, requireAdministrator, and highestAvailable values; specifying a requestedExecutionLevel node disabling file and registry virtualization; omitting the node if you want to use virtualization for backward compatibility; and the Visual C++ linker embedding an asInvoker UAC fragment into the manifest by default.  2 3

Recent articles sharing the same tags. Deepen your understanding with closely related topics.

These topic pages place the article in a broader service and decision context.

This article connects naturally to the following service pages.

Frequently Asked Questions

Common questions about the topic of this article.

Why can I see the value in regedit, but the app says it doesn't exist?
The classic cause is that the app and regedit are looking at different registry views. On 64-bit Windows, regedit is a 64-bit process and displays things relative to the 64-bit view, including Wow6432Node (the physical location of the 32-bit view). When a 32-bit app opens HKLM\Software, on the other hand, the WOW64 registry redirector steers it to the Wow6432Node side, so a value that exists only in the 64-bit view is "not there" as far as the app is concerned. First check whether the path of the value you can see in regedit contains Wow6432Node, then compare both views with reg query using /reg:64 and /reg:32 — that gets you to the answer fastest.
Is it acceptable to access the Wow6432Node path directly from code?
You should avoid it. Microsoft's official documentation states explicitly that the physical location of the redirection target is reserved by the system, that it may change in future, and that applications should not access it directly. In fact, Windows 10 on ARM uses a different physical location, WowAA32Node, for 32-bit ARM apps, so hard-coding Wow6432Node breaks on ARM. If you need to access the other view, use the official mechanisms: the KEY_WOW64_64KEY/KEY_WOW64_32KEY flags, or RegistryView in .NET.
Why is the value I supposedly wrote to HKLM sitting in the VirtualStore under HKCU?
That is UAC registry virtualization at work. When a 32-bit interactive process without write permission writes under HKLM\Software while having no requestedExecutionLevel in its manifest, instead of failing the write is diverted to a per-user virtual store (HKEY_USERS\<SID>_Classes\VirtualStore\Machine\Software, which appears under HKCU\Software\Classes\VirtualStore in regedit). Because that same process sees a merged view of the virtual store and the original location when it reads back, everything appears to work — but 64-bit processes and services cannot see the value, producing the strange failure mode where "the settings differ from user to user". Virtualization is an interim technology for rescuing legacy apps, so a new application must never depend on it.
How do I make the registry bitness (32-bit/64-bit view) explicit in C#?
Pass a RegistryView to RegistryKey.OpenBaseKey. Specify RegistryView.Registry64 and you can read and write the 64-bit view even from a 32-bit process; specify RegistryView.Registry32 and you can read and write the 32-bit view (the Wow6432Node side) even from a 64-bit process. RegistryView.Default leaves it up to the process bitness, so in configurations where the bitness varies with the runtime environment — an AnyCPU build, for instance — it is safer to state explicitly which view you are reading. Note that on a 32-bit OS a request for Registry64 returns the 32-bit view by design, so the same code still works if you have remaining 32-bit OS support.
How do I disable registry virtualization, or check whether it is in effect?
On the application side, specifying a requestedExecutionLevel in the manifest (asInvoker is fine) disables file and registry virtualization for that process. On the administration side, the reg flags command lets you set and query the per-key REG_KEY_DONT_VIRTUALIZE flag. If you are investigating an app that is already running, the quick route is to check per-process virtualization status via the "UAC virtualization" column in Task Manager, and to look at whether diverted values have piled up under HKCU\Software\Classes\VirtualStore. As a permanent fix, we recommend correcting the design so that it does not write to HKLM in the first place (per-user settings belong in HKCU or AppData).

Author Profile

Profile page for the article author.

Go Komura

Representative of KomuraSoft LLC

Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.

Back to the Blog