Registry-32-Bit/64-Bit-Umleitung und Virtualisierungsfallen — Wow6432Node und das Problem „Der Wert, den ich geschrieben habe, ist nicht da“

· · Registry, WOW64, Wow6432Node, UAC, 64-Bit-Migration, Windows, C#, Fehlersuche

„Die Anwendung kann den Lizenzschlüssel, den der Installer geschrieben hat, beim Start nicht lesen. Dabei ist der Wert in regedit eindeutig vorhanden.“ — Das war der Bericht, den wir während der Unterstützung der 64-Bit-Migration einer Anlagenintegrationsanwendung erhielten. Bei der Untersuchung stellte sich heraus: Der Wert lag unter HKLM\Software\Wow6432Node\<Firmenname>. Die frisch als 64-Bit neu gebaute Anwendung schaute nach HKLM\Software\<Firmenname>, wo sich überhaupt nichts befand.

Diese Art von „der Wert, den ich geschrieben habe, ist nicht da“ oder „regedit sieht ihn, aber die App nicht“ bleibt ein Rätsel, egal wie genau Sie hinsehen, wenn Sie nicht zwei Mechanismen kennen: dass die Registry unter 64-Bit-Windows je nach Prozess-Bitness an einer anderen Stelle erscheint (WOW64-Registry-Umleitung), und dass ein Schreibvorgang ohne ausreichende Berechtigungen still an eine andere Stelle umgeleitet wird (UAC-Registry-Virtualisierung). Schlimmer noch: Beide „gelingen ohne Fehler“, sodass das Problem erst genau in dem Moment auftaucht, in dem der Ort des Schreibens und der Ort des Lesens auseinandergehen — also zum Zeitpunkt einer 64-Bit-Migration oder einer Installer-Änderung.

Dieser Artikel richtet sich an Entwickler von Windows-Geschäftsanwendungen. Er legt dar, wie die WOW64-Registry-Umleitung funktioniert, welche Schlüssel umgeleitet werden und welche geteilt sind, die Bedingungen, die die UAC-Registry-Virtualisierung auslösen, den realen Schaden, der in Installern und der COM-Registrierung auftritt, sowie die korrekte Art, die Ansicht in C#/C++/reg.exe explizit zu machen — alles belegt durch die offizielle Dokumentation.

1. Zunächst das Fazit

  • Die Registry unter 64-Bit-Windows hat eine 64-Bit- und eine 32-Bit-Ansicht, und der Zugriff eines 32-Bit-Prozesses auf HKLM\Software wird vom Registry-Umleiter transparent zur physischen Position HKLM\Software\Wow6432Node gelenkt. Die Anwendung sieht weder einen Fehler noch eine Warnung.1
  • Die physische Position Wow6432Node ist vom System reserviert. Sie dürfen den Pfad nicht festcodieren und direkt darauf zugreifen (unter Windows 10 on ARM wird für 32-Bit-ARM eine andere Position, WowAA32Node, verwendet). Greifen Sie über die offiziellen Mechanismen (die unten beschriebenen Flags und RegistryView) auf die andere Ansicht zu.12
  • Nur manche Schlüssel werden umgeleitet; andere sind geteilt. Unter Windows 7 und neuer ist HKLM\SOFTWARE „umgeleitet“, HKLM\SOFTWARE\Classes ist „geteilt“, doch CLSID und Interface darunter sind erneut „umgeleitet“ — eine verschachtelte Struktur.3
  • Um auf die andere Ansicht zuzugreifen: Geben Sie in Win32 KEY_WOW64_64KEY / KEY_WOW64_32KEY im samDesired-Argument von RegOpenKeyEx und Ähnlichem an; übergeben Sie in .NET RegistryView.Registry64 / Registry32 an RegistryKey.OpenBaseKey; verwenden Sie auf der Befehlszeile reg.exe mit /reg:64 / /reg:32.245
  • Schreibt ein 32-Bit-interaktiver Prozess ohne Administratorrechte nach HKLM\Software, kann die UAC-Registry-Virtualisierung den Schreibvorgang nach HKEY_USERS\<SID>_Classes\VirtualStore\Machine\Software umleiten. Die Falle besteht darin, dass der Schreibvorgang „gelingt“ und, liest derselbe Prozess zurück, die zusammengeführte Ansicht ihn sichtbar macht.6
  • Die Angabe von requestedExecutionLevel im Manifest deaktiviert Datei- und Registry-Virtualisierung. Umgekehrt formuliert: Nur Legacy-EXEs ohne Manifest unterliegen der Virtualisierung.7
  • Registry-Virtualisierung ist eine Übergangstechnologie zur Kompatibilität, und Microsoft erklärt ausdrücklich die Absicht, sie aus einer künftigen Windows-Version zu entfernen. Eine neue Anwendung darf sich nicht darauf verlassen. Als Designprinzip gilt: „nicht nach HKLM schreiben“ ist die richtige Antwort.6
  • Die COM-CLSID-Registrierung (HKCR\CLSID = HKLM\Software\Classes\CLSID) ist nach Bitness getrennt. Die Registrierung eines 32-Bit-COM-Servers ist für einen 64-Bit-Client unsichtbar — die Standardursache für „Klasse nicht registriert (0x80040154)“.3

2. WOW64-Registry-Umleitung — Wohin schaut ein 32-Bit-Prozess tatsächlich?

64-Bit-Windows führt 32-Bit-Anwendungen auf einem Subsystem namens WOW64 aus. Dabei präsentiert der Registry-Umleiter getrennte logische Ansichten für 32-Bit- und 64-Bit-Prozesse. Das Kernstück des Mechanismus ist, dass beide über dieselbe API denselben Schlüsselnamen angeben (HKEY_LOCAL_MACHINE\Software\...), doch die tatsächlich gelesene und geschriebene physische Position unterscheidet sich.1

Die physische Position eines umgeleiteten Schlüssels ist Wow6432Node. Zum Beispiel wird HKEY_LOCAL_MACHINE\Software für einen 32-Bit-Prozess physisch auf HKEY_LOCAL_MACHINE\Software\Wow6432Node abgebildet. Diese Abbildung ist für die Anwendung vollständig transparent, sodass eine 32-Bit-App die Registry bearbeiten kann, „als liefe sie auf 32-Bit-Windows“.1

Tabellarisch dargestellt, wer wohin schaut, ergibt sich Folgendes.

Aufrufer Im Code angegebener Pfad Tatsächlich gelesene/geschriebene physische Position
64-Bit-Prozess HKLM\Software\MyApp HKLM\Software\MyApp
32-Bit-Prozess HKLM\Software\MyApp HKLM\Software\Wow6432Node\MyApp
32-Bit-Prozess + KEY_WOW64_64KEY (RegistryView.Registry64) HKLM\Software\MyApp HKLM\Software\MyApp
64-Bit-Prozess + KEY_WOW64_32KEY (RegistryView.Registry32) HKLM\Software\MyApp HKLM\Software\Wow6432Node\MyApp
Schreiben durch einen 32-Bit-interaktiven Prozess, Standardrechte, kein Manifest HKLM\Software\MyApp HKCU\Software\Classes\VirtualStore\Machine\Software\Wow6432Node\MyApp (virtualisiert nach der WOW64-Umleitung; siehe Kapitel 5)
regedit (ein 64-Bit-Prozess) Wird relativ zur 64-Bit-Ansicht angezeigt. Wow6432Node ist ebenfalls als gewöhnlicher physischer Schlüssel sichtbar

Die eingangs geschilderte Unglücksgeschichte ist genau diese Tabelle. Der vom 32-Bit-Installer geschriebene Wert liegt physisch unter Wow6432Node, während die inzwischen 64-Bit-Anwendung das schlichte HKLM\Software liest. Regedit sieht beides, sodass der Bericht vor Ort „es ist in regedit da“ und das Symptom „die App kann es nicht sehen“ vollkommen widerspruchsfrei zusammenpassen.

Sie könnten nun versucht sein, den Pfad Wow6432Node festzucodieren, um die Sache zu bereinigen, doch das ist ein Anti-Pattern, das die offizielle Dokumentation eindeutig verbietet. Die physische Position des Umleitungsziels ist vom System reserviert und kann sich ändern. Tatsächlich ist unter Windows 10 on ARM das Umleitungsziel für 32-Bit-ARM-Apps ein anderer Schlüssel, WowAA32Node.12 Wollen Sie die andere Ansicht lesen, verwenden Sie die offiziellen Mechanismen aus Kapitel 4.

Als feineres Detail führt WOW64 auch eine Korrektur durch, bei der REG_SZ/REG_EXPAND_SZ-Zeichenketten, die mit %ProgramFiles% beginnen und von einer 32-Bit-App geschrieben werden, durch %ProgramFiles(x86)% ersetzt werden (nur wenn die Groß-/Kleinschreibung exakt übereinstimmt).1 Darüber hinaus gab es in der Vista/XP-Ära die „Registry-Reflektion“, die Schlüssel zwischen der 32-Bit- und der 64-Bit-Ansicht kopierte und synchronisierte, doch diese wurde unter Windows 7 / Windows Server 2008 R2 entfernt. Achten Sie beim Lesen älterer Erklärartikel auf diesen Unterschied in den Voraussetzungen.1

3. Schlüssel, die umgeleitet werden, und Schlüssel, die geteilt werden

Nicht jeder Schlüssel wird umgeleitet. Manche Schlüssel teilen eine einzige physische Kopie zwischen beiden Ansichten. Hier ein Auszug aus der Liste im offiziellen Dokument „Registry Keys Affected by WOW64“, der die für die Entwicklung von Geschäftsanwendungen relevantesten Einträge abdeckt (die Spalte für Windows 7 / Server 2008 R2 und neuer; Unterschlüssel übernehmen im Allgemeinen das Verhalten ihres übergeordneten Schlüssels).3

Schlüssel Verhalten unter Windows 7 und neuer
HKLM\SOFTWARE Umgeleitet
HKLM\SOFTWARE\Classes Geteilt
HKLM\SOFTWARE\Classes\CLSID Umgeleitet
HKLM\SOFTWARE\Classes\Interface Umgeleitet
HKLM\SOFTWARE\Classes\DirectShow / Media Type / MediaFoundation Umgeleitet
HKLM\SOFTWARE\Clients Geteilt
HKLM\SOFTWARE\Microsoft\COM3 / EventSystem / OLE / RPC Geteilt
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths Geteilt
HKLM\SOFTWARE\Policies Geteilt
HKCU\SOFTWARE Geteilt
HKCU\SOFTWARE\Classes Geteilt
HKCU\SOFTWARE\Classes\CLSID / Interface Umgeleitet

Was hervorsticht, ist die Verschachtelung. HKLM\SOFTWARE wird umgeleitet, doch Classes darunter fällt zurück auf geteilt, und CLSID und Interface darunter werden erneut umgeleitet. Mit dem oberflächlichen Verständnis, dass „alles unter Software zu Wow6432Node geht“, lässt sich der Unterschied im Verhalten zwischen Dateierweiterungszuordnungen (direkt unter Classes, geteilt) und COM-Klassenregistrierung (Classes\CLSID, umgeleitet) nicht erklären. HKCU ist grundsätzlich geteilt, was eine weitere wichtige praktische Konsequenz ergibt: Solange Sie benutzerbezogene Einstellungen in HKCU halten, treten Bitness-Probleme so gut wie gar nicht auf.3

Beachten Sie auch, dass Flags wie KEY_WOW64_64KEY bei geteilten Schlüsseln keine Wirkung haben. Weil es von einem geteilten Schlüssel von vornherein nur eine Kopie gibt, ergibt ein Wechsel der Ansicht keinen Sinn.2

4. Die andere Ansicht explizit lesen — die korrekte Schreibweise in reg.exe, C# und C++

Der schnellste Weg festzustellen, „in welcher Ansicht der Wert liegt“, sind die Optionen /reg:64 / /reg:32 von reg.exe. Sie greifen jeweils explizit auf die 64-Bit- bzw. die 32-Bit-Ansicht zu.5

:: Die 64-Bit-Ansicht lesen (schlichtes HKLM\Software)
reg query "HKLM\SOFTWARE\KomuraSoft\DeviceLink" /v LicenseKey /reg:64

:: Die 32-Bit-Ansicht lesen (physisch unter Wow6432Node)
reg query "HKLM\SOFTWARE\KomuraSoft\DeviceLink" /v LicenseKey /reg:32

Führen Sie diese beiden Zeilen aus, und existiert der Wert nur in einer davon, haben Sie bestätigt, dass „die Bitness von Schreiber und Leser nicht übereinstimmt“. Der entscheidende Punkt ist, nur die Ansicht gegenüber demselben logischen Pfad zu wechseln, statt Wow6432Node von Hand in den Pfad zu schreiben.

In C# (.NET) übergeben Sie ein RegistryView an RegistryKey.OpenBaseKey. Es gibt drei Werte — Registry64 (256), Registry32 (512) und Default (0) —, und sie lassen sich in OpenBaseKey / OpenRemoteBaseKey / FromHandle angeben.4

using Microsoft.Win32;

// Die 64-Bit-Ansicht selbst aus einem 32-Bit-Prozess lesen
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
using (var key = baseKey.OpenSubKey(@"SOFTWARE\KomuraSoft\DeviceLink"))
{
    var license = key?.GetValue("LicenseKey") as string;
}

// Die 32-Bit-Ansicht (die Wow6432Node-Seite) aus einem 64-Bit-Prozess lesen
// — verwendet, um zum Beispiel Werte querzulesen, die ein Installer aus der 32-Bit-Ära geschrieben hat
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 überlässt es der Prozess-Bitness. Weil eine AnyCPU-.NET-Anwendung je nach Laufzeitumgebung 32-Bit oder 64-Bit sein kann, verhindert die explizite Angabe im Code, welche Ansicht Sie beim Umgang mit maschinenweiten Daten unter HKLM lesen, den Vorfall, bei dem eine Änderung der Build-Einstellungen (das Umschalten von Prefer 32-bit oder eine 64-Bit-Migration) plötzlich ändert, wie die Registry aussieht. Beachten Sie außerdem, dass eine Anforderung von Registry64 auf einem 32-Bit-Betriebssystem konstruktionsbedingt Schlüssel aus der 32-Bit-Ansicht zurückgibt, sodass derselbe Code auch dann sicher funktioniert, wenn noch 32-Bit-Betriebssystemunterstützung besteht.4

In C++ (der Win32-API) verknüpfen Sie KEY_WOW64_64KEY (0x0100) oder KEY_WOW64_32KEY (0x0200) per OR in das samDesired-Argument von RegOpenKeyEx / RegCreateKeyEx / RegDeleteKeyEx. Die gleichzeitige Angabe beider schlägt mit ERROR_INVALID_PARAMETER fehl.2

HKEY hKey = nullptr;
// Einen Schlüssel in der 64-Bit-Ansicht aus einem 32-Bit-Prozess öffnen
LSTATUS st = RegOpenKeyExW(
    HKEY_LOCAL_MACHINE,
    L"SOFTWARE\\KomuraSoft\\DeviceLink",
    0,
    KEY_READ | KEY_WOW64_64KEY,   // die Ansicht wird hier explizit gemacht
    &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);
}

Die offizielle Dokumentation nennt zwei Vorbehalte. Haben Sie die andere Ansicht einmal mit einem Flag geöffnet, geben Sie dasselbe Flag weiterhin bei Operationen auf den darunterliegenden Unterschlüsseln an (Erstellen, Löschen, Öffnen). Ein Vermischen führt zu unerwartetem Verhalten. Wollen Sie außerdem die Schlüssel beider Ansichten vollständig aufzählen, müssen Sie in zwei Durchgängen aufzählen — einmal mit einem über KEY_WOW64_64KEY geöffneten Handle und einmal mit KEY_WOW64_32KEY. Beachten Sie auch, dass RegDeleteKey (ohne das Ex) nicht auf die andere Ansicht zugreifen kann.2

5. UAC-Registry-Virtualisierung — Ich habe nach HKLM geschrieben, warum liegt es dann im VirtualStore?

Der andere Mechanismus, der leicht mit der Umleitung verwechselt wird, ist die UAC-Registry-Virtualisierung. Dabei geht es nicht um Bitness, sondern um Berechtigungen: Seit Vista ist sie eine Kompatibilitätstechnologie zur Rettung von Legacy-Anwendungen, die unter der Annahme von Administratorrechten geschrieben wurden.6

So verhält sie sich. Versucht ein Prozess ohne Schreibberechtigung, einen Wert zu schreiben oder einen Unterschlüssel unter HKLM\Software zu erstellen, wird der Schreibvorgang statt mit „Zugriff verweigert“ fehlzuschlagen in einen benutzerbezogenen virtuellen Speicher umgeleitet, HKEY_USERS\<Benutzer-SID>_Classes\VirtualStore\Machine\Software (in regedit sichtbar unter HKCU\Software\Classes\VirtualStore\Machine\Software). Beim Lesen wird darüber hinaus eine zusammengeführte Ansicht der Werte des virtuellen Speichers und der Werte des ursprünglichen globalen Speichers zurückgegeben, wobei bei gleichnamigen Werten der virtuelle Speicher Vorrang hat.6 Beachten Sie, dass für einen 32-Bit-Prozess auf einem 64-Bit-Betriebssystem zuerst die WOW64-Umleitung aus Kapitel 3 greift, sodass das tatsächliche Ziel eines Schreibvorgangs nach HKLM\Software\MyApp zu VirtualStore\Machine\Software\Wow6432Node\MyApp wird. Untersuchen Sie unter VirtualStore, prüfen Sie immer auch die Wow6432Node-Seite zusätzlich zur schlichten Software-Seite.

Mit anderen Worten: Der Prozess, der den Schreibvorgang durchgeführt hat, kann lesen und schreiben, als wäre nichts geschehen. Das ist die wahre Identität jener benutzerabhängigen Eigenartigkeiten: „auf der Entwicklungsmaschine (als Administrator ausgeführt) funktioniert es einwandfrei, doch die Einstellungen gehen nur in der Standardbenutzerumgebung des Kunden schief“, oder „es funktioniert, wenn Benutzer A sich anmeldet, kehrt aber für Benutzer B auf die Standardwerte zurück“. Der virtuelle Speicher ist Teil des Benutzerprofils (NTUSER.DAT und so weiter), daher unterscheidet sich sein Inhalt je Benutzer (zur Struktur des Profils siehe „Introduction to Windows User Profiles - AppData and NTUSER.DAT“).

Die Bedingungen, unter denen die Virtualisierung greift, sind eng gefasst und ausdrücklich in der offiziellen Dokumentation festgehalten.6

  • Sie gilt nur für Operationen eines 32-Bit-interaktiven Prozesses auf Schlüsseln unter HKLM\Software, die ein Administrator schreiben könnte
  • Folgendes ist ausgenommen: 64-Bit-Prozesse, nicht-interaktive Prozesse wie Dienste, Operationen während der Impersonation eines Benutzers, Treiber, und Prozesse, die requestedExecutionLevel im Manifest angeben
  • Auch alles unter HKLM\Software\Classes, HKLM\Software\Microsoft\Windows und HKLM\Software\Microsoft\Windows NT ist ausgenommen

Der Punkt, der in der Praxis am stärksten zubeißt, ist, dass „sich das Verhalten je nach Vorhandensein eines Manifests ändert“. Der C++-Linker von Visual Studio bettet standardmäßig ein asInvoker-UAC-Fragment in das Manifest ein7, sodass eine mit einer modernen Toolchain gebaute EXE von vornherein von der Virtualisierung ausgenommen ist. Auf die Virtualisierung treffen Sie an Standorten, an denen Legacy-EXEs ohne Manifest laufen — gebaut mit VB6, altem Delphi oder altem VC++ — unter 64-Bit-Windows. Umgekehrt gibt es eine Migrationsfalle in die andere Richtung: In dem Moment, in dem Sie einer Legacy-EXE „einfach ein Manifest hinzufügen“ oder sie als 64-Bit neu bauen, schaltet sich die Virtualisierung ab, und die Schreibvorgänge, die bislang „funktionierten“, beginnen nun tatsächlich mit Zugriff verweigert zu scheitern (oder still nicht zu schreiben). Die drei Werte von requestedExecutionLevel (asInvoker / highestAvailable / requireAdministrator) und die Überlegungen hinter dem Berechtigungsdesign werden ausführlich in „When Do You Actually Need Administrator Privileges on Windows? - UAC, Protected Areas, and How to Tell by Design“ behandelt.

Hervorzuheben ist, dass die offizielle Dokumentation ausdrücklich feststellt, dass die Virtualisierung eine Übergangstechnologie zur Kompatibilität ist und Microsoft beabsichtigt, sie aus einer künftigen Windows-Version zu entfernen. Sich in einer Neuentwicklung auf dieses Verhalten zu verlassen, kommt nicht infrage, und das Designprinzip lautet: „Eine Anwendung schreibt nicht in sensible Systembereiche (HKLM). Daten gehören entweder an einen benutzerbezogenen Ort oder an einen gemeinsamen Ort mit angemessenen ACLs.“6 Wo was zu speichern ist, wird in „How to Choose Where a Windows App Stores Local Data — A Decision Table for SQLite / JSON / Registry / Access“ dargelegt.

Es gibt auch Flags zur Steuerung der Virtualisierung pro Schlüssel (REG_KEY_DONT_VIRTUALIZE / REG_KEY_DONT_SILENT_FAIL / REG_KEY_RECURSE_FLAG), die sich mit reg.exe abfragen und setzen lassen, etwa mit reg flags HKLM\Software\AppKey1 QUERY.6 Bei der Untersuchung eines Problems sind zwei schnelle Prüfungen: die Spalte „UAC-Virtualisierung“ auf der Registerkarte Details des Task-Managers anzeigen, um den Virtualisierungsstatus eines Prozesses zu sehen, und unter HKCU\Software\Classes\VirtualStore nach umgeleiteten Rückständen zu schauen.

6. Der reale Schaden bei Installern und COM-Registrierung

Die Orte, an denen diese beiden Mechanismen am ehesten in realen Schaden ausbrechen, sind Installer und COM-Registrierung.

Das Installer-Bitness-Problem. Einstellungen, die ein 32-Bit-Installer (ein 32-Bit-MSI oder eine 32-Bit-Setup-EXE) nach HKLM\Software\<Firmenname> schreibt, landen physisch unter Wow6432Node. Machen Sie die Anwendung selbst 64-Bit, verwenden aber den 32-Bit-Installer unverändert weiter, erhalten Sie genau die eingangs geschilderte Unglücksgeschichte: „der Installer hat es geschrieben, die Anwendung kann es nicht lesen“. Das umgekehrte Muster (ein 64-Bit-Installer plus eine 32-Bit-Anwendung) verhält sich gleich. Die Abhilfen bestehen darin, Installer und Anwendung darauf abzustimmen, „welche Ansicht geschrieben und welche gelesen wird“, und während des Übergangs zu 64-Bit ein Querlesen aus der alten Position mit RegistryView.Registry32 aus Kapitel 4 zu implementieren.

Das COM-Registrierungs-Bitness-Problem. Wie die Tabelle in Kapitel 3 zeigt, unterliegt HKLM\Software\Classes\CLSID (die HKLM-Seite von HKCR\CLSID) der Umleitung. Das bedeutet, die CLSID-Registrierung eines 32-Bit-COM-Servers landet in der 32-Bit-Ansicht und eine 64-Bit-Registrierung in der 64-Bit-Ansicht, und keine kann die andere sehen.3 Ein 64-Bit-Prozess könnte einen 32-Bit-DLL-In-Process-COM-Server ohnehin nicht laden, sodass die Trennung selbst sinnvoll ist — doch im Feld trifft es Sie in Form von „ich habe es mit regsvr32 registriert, doch der Client bekommt 0x80040154 (Klasse nicht registriert)“. regsvr32 selbst existiert ebenfalls in einer 32-Bit-Version (auf der SysWOW64-Seite) und einer 64-Bit-Version (auf der System32-Seite), und welche Sie verwendet haben, bestimmt, welche Ansicht geschrieben wird. Das vollständige Bild der Fallstricke, die aus der Kombination von COM-Registrierung und Bitness entstehen, ist in „Registration and Bitness Pitfalls in COM/OCX/ActiveX Development“ dargelegt, und die Option, auf die Registry-Registrierung ganz zu verzichten, wird in „What Is Reg-Free COM - Using COM Without Registration“ behandelt.

Die COM-Welt trägt noch ein weiteres Stück historischen Ballasts. In der Vista/XP-Ära wurden CLSID und ähnliche Schlüssel mit „Umleitung plus Reflektion (Synchronisierung zwischen den beiden Ansichten)“ behandelt, doch die Reflektion wurde unter Windows 7 entfernt, und heute ist es rein eine Trennung nach Ansicht.3 Es sind auch eine Reihe kompatibilitätsbedingter symbolischer Links definiert, etwa HKLM\SOFTWARE\Wow6432Node\ClassesHKLM\SOFTWARE\Classes\Wow6432Node, doch diese existieren zur Rettung bestehender Anwendungen, die Wow6432Node festcodiert hatten, und sind nichts, das neue Anwendungen verwenden sollten.3

Beachten Sie auch, dass selbst nach Überwindung der Registry-Trennung ein separater Satz von Namensauflösungsregeln beim Laden der DLL selbst wartet. Für Untersuchungen im Stil von „nicht gefunden“ ist „How Windows DLL Name Resolution Works - Search Order and SxS“ ein nützlicher Begleiter.

7. Fehlersuche — mit Procmon beobachten, wo tatsächlich gelesen wurde

Selbst wenn Sie wissen, wie die Mechanismen funktionieren, erfordert die Feststellung, „welchen physischen Schlüssel dieser Prozess für den Fehlschlag vor Ihnen tatsächlich gelesen hat“, eine Beobachtung. Das mächtigste Werkzeug hierfür ist Sysinternals Process Monitor (Procmon).

Das Verfahren ist einfach.

  1. Starten Sie Procmon und fügen Sie den Filter Process Name is <Zielanwendung>.exe hinzu
  2. Beschränken Sie über die Symbolleiste die Anzeige auf Registry-Operationen (auch ein Filter Operation begins with Reg funktioniert)
  3. Reproduzieren Sie den problematischen Vorgang in der Anwendung und betrachten Sie die Zeilen RegOpenKey / RegQueryValue / RegSetValue

Der entscheidende Punkt ist, dass die Path-Spalte von Procmon den physischen Pfad nach Auflösung der Umleitung anzeigt. Auch wenn die 32-Bit-App glaubt, HKLM\Software\MyApp geöffnet zu haben, zeigt Procmon HKLM\SOFTWARE\WOW6432Node\MyApp. Reihen sich dort NAME NOT FOUND-Einträge auf, ist „welcher Schlüssel in welcher Ansicht fehlt“ auf einen Blick klar; und landet ein Schreibvorgang in HKCU\Software\Classes\VirtualStore\..., können Sie beobachten, wie die Virtualisierung einsetzt. Bei einer COM-0x80040154-Untersuchung lässt sich sogar nachverfolgen, in welcher Ansicht das fehlgeschlagene Öffnen von CLSID\{...} erfolgte. Details zum Entwurf von Procmon-Filtern und zum Lesen der Ausgabe finden Sie in „A Practical Guide to Process Monitor (ProcMon)“.

8. Praktische Faustregeln (Entscheidungstabelle)

Situation Was zu tun ist Begründung / Hinweise
„regedit sieht ihn, die App aber nicht“ Beide Ansichten mit reg query ... /reg:64 und /reg:32 vergleichen Zuerst feststellen, welche Ansicht den Wert enthält5
Sowohl 32-Bit- als auch 64-Bit-eigene Prozesse lesen dieselbe HKLM-Einstellung Die Ansicht auf der Schreibseite fixieren (z. B. die 64-Bit-Ansicht) und jeden Leser dieselbe Ansicht explizit angeben lassen Auf RegistryView.Registry64 / KEY_WOW64_64KEY vereinheitlichen42
Versuchung, Wow6432Node im Code festzucodieren Nicht tun. Durch die ansichtsspezifizierenden APIs ersetzen Die physische Position ist vom System reserviert. Auf ARM wird sie zu WowAA32Node12
Wo benutzerbezogene Einstellungen abgelegt werden sollen In HKCU (oder AppData) ablegen HKCU ist ein geteilter Schlüssel, daher keine Bitness- und keine Berechtigungsprobleme3
Die Einstellungen einer Legacy-32-Bit-App „unterscheiden sich von Benutzer zu Benutzer“ HKCU\Software\Classes\VirtualStore prüfen Das klassische Muster, bei dem durch Virtualisierung umgeleitete Werte sich pro Benutzer ansammeln6
Einer Legacy-EXE ein Manifest hinzufügen / sie 64-Bit machen Vorher die HKLM-Schreibstellen inventarisieren Die Virtualisierung wird deaktiviert, und Schreibvorgänge, die bisher „funktionierten“, beginnen zu scheitern67
COM 0x80040154 Die Bitness von Client und Server prüfen und die Registrierung mit dem passenden regsvr32/der passenden Ansicht verifizieren Die CLSID-Registrierung ist nach Ansicht getrennt3
Kann nicht feststellen, woher gelesen wurde Mit Procmon den physischen Pfad und das Ergebnis (NAME NOT FOUND und so weiter) beobachten Aufhören zu raten und die Fakten betrachten — der kürzeste Weg

9. Zusammenfassung

  • Die Registry unter 64-Bit-Windows hat zwei Ansichten, und HKLM\Software eines 32-Bit-Prozesses wird transparent zu Wow6432Node umgeleitet. Sie ist der Hauptverdächtige für „der Wert, den ich geschrieben habe, ist nicht da“.
  • Die Umleitung gilt nicht für jeden Schlüssel. Merken Sie sich die verschachtelte Struktur: Classes ist geteilt, CLSID / Interface darunter sind umgeleitet, und HKCU ist fast vollständig geteilt.
  • Codieren Sie Wow6432Node niemals fest. Machen Sie die Ansicht explizit mit /reg:64 /reg:32 von reg.exe, RegistryView von .NET, oder KEY_WOW64_64KEY / KEY_WOW64_32KEY von Win32.
  • Die UAC-Registry-Virtualisierung leitet unterprivilegierte HKLM-Schreibvorgänge von manifestlosen 32-Bit-interaktiven Prozessen still in den VirtualStore um. Es ist eine Übergangstechnologie zur Rettung von Legacy-Apps, und eine neue Anwendung darf sich nicht darauf verlassen.
  • Stimmen Sie ab, „welche Ansicht verwendet wird“, zwischen Installer und Anwendung sowie zwischen COM-Server und Client. Entwerfen Sie bei der Migration zu 64-Bit ein Querlesen aus der alten Ansicht.
  • Beobachten Sie im Zweifel den physischen Pfad mit Procmon. Beobachtung ist schneller und zuverlässiger als Raten.

Verwandte Artikel

Verwandte Beratungsbereiche

Die KomuraSoft LLC übernimmt die Untersuchung von Fehlern wie „der Wert, den ich in die Registry geschrieben habe, lässt sich nicht lesen“ und „die COM-Registrierung wird nicht gefunden“, das Design von 64-Bit-Migrationen für 32-Bit-Anwendungs- und COM-Komponenten-Assets sowie die kundenspezifische Entwicklung von Windows-Geschäftsanwendungen einschließlich Anlagenintegrationssoftware.

  1. Microsoft Learn, Registry Redirector. Dazu, dass der Registry-Umleiter 32-Bit- und 64-Bit-Anwendungen getrennte logische Ansichten transparent präsentiert; dazu, dass HKEY_LOCAL_MACHINE\Software nach HKEY_LOCAL_MACHINE\Software\Wow6432Node umgeleitet wird; dazu, dass die physische Position vom System reserviert ist und Anwendungen nicht direkt darauf zugreifen sollten; dazu, dass 32-Bit-ARM-Schlüssel unter Windows 10 on ARM auf WowAA32Node abgebildet werden; zum Ersetzen von %ProgramFiles%-Zeichenketten; und dazu, dass die Reflektion unter Windows 7 / Windows Server 2008 R2 entfernt wurde.  2 3 4 5 6 7 8

  2. Microsoft Learn, Accessing an Alternate Registry View. Zur Bedeutung von KEY_WOW64_64KEY (0x0100) und KEY_WOW64_32KEY (0x0200); zur Angabe im samDesired-Argument von RegCreateKeyEx, RegDeleteKeyEx und RegOpenKeyEx; dazu, dass die gleichzeitige Angabe beider Flags zu ERROR_INVALID_PARAMETER führt; dazu, dass die Flags bei geteilten Schlüsseln keine Wirkung haben; dazu, dass bei Operationen auf Unterschlüsseln weiterhin dasselbe Flag verwendet werden sollte; dazu, dass die Aufzählung aller Schlüssel in zwei Durchgängen erfolgen muss; und dazu, dass Wow6432Node/WowAA32Node reservierte Schlüssel sind.  2 3 4 5 6 7 8

  3. Microsoft Learn, Registry Keys Affected by WOW64. Zur Liste umgeleiteter und geteilter Schlüssel (unter Windows 7 und neuer ist HKLM\SOFTWARE umgeleitet, HKLM\SOFTWARE\Classes ist geteilt, Classes\CLSID, Interface, DirectShow und andere sind umgeleitet, während Clients, COM3, OLE, RPC, App Paths, Policies, HKCU\SOFTWARE und andere geteilt sind); dazu, dass Unterschlüssel das Verhalten ihres übergeordneten Schlüssels übernehmen; dazu, dass HKCR eine zusammengeführte Ansicht der HKLM- und HKCU-Classes-Schlüssel ist; und dazu, dass die kompatibilitätsbedingten symbolischen Links rund um Wow6432Node zur Rettung bestehender Anwendungen dienen und nicht für neue Anwendungen gedacht sind.  2 3 4 5 6 7 8 9

  4. Microsoft Learn, RegistryView Enum (Microsoft.Win32). Zu den Werten Default (0), Registry64 (256) und Registry32 (512) der RegistryView-Enumeration; dazu, dass sich die Ansicht in OpenBaseKey, OpenRemoteBaseKey und FromHandle angeben lässt; und dazu, dass eine Anforderung der 64-Bit-Ansicht auf einem 32-Bit-Betriebssystem Schlüssel aus der 32-Bit-Ansicht zurückgibt.  2 3 4

  5. Microsoft Learn, reg query. Dazu, dass die Option /reg:32 des Befehls reg query auf den Schlüssel in der 32-Bit-Registry-Ansicht zugreift und /reg:64 in der 64-Bit-Registry-Ansicht.  2 3

  6. Microsoft Learn, Registry Virtualization. Dazu, dass Schreibvorgänge nach HKLM\Software nach HKEY_USERS<Benutzer-SID>_Classes\VirtualStore\Machine\Software umgeleitet werden; dazu, dass Lesevorgänge eine zusammengeführte Ansicht zurückgeben, in der der virtuelle Speicher Vorrang hat; dazu, dass die Virtualisierung nur für 32-Bit-interaktive Prozesse, nur unter HKLM\Software und nur für Schlüssel gilt, die ein Administrator schreiben kann; dazu, dass sie für 64-Bit-Prozesse, Dienste, Operationen während der Impersonation, Prozesse mit Angabe von requestedExecutionLevel und Unterschlüssel wie Classes deaktiviert ist; dazu, dass es sich um eine Übergangstechnologie zur Kompatibilität handelt, die künftig entfernt werden soll und auf die sich Anwendungen nicht verlassen sollten; und zur Steuerung von REG_KEY_DONT_VIRTUALIZE und ähnlichen Flags über reg flags.  2 3 4 5 6 7 8 9

  7. Microsoft Learn, Application manifests. Zur Bedeutung der Werte asInvoker, requireAdministrator und highestAvailable des Elements requestedExecutionLevel; dazu, dass die Angabe eines requestedExecutionLevel-Knotens Datei- und Registry-Virtualisierung deaktiviert; dazu, dass der Knoten weggelassen werden sollte, wenn zur Abwärtskompatibilität Virtualisierung genutzt werden soll; und dazu, dass der Visual-C++-Linker standardmäßig ein asInvoker-UAC-Fragment in das Manifest einbettet.  2 3

Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.

Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.

Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.

Häufige Fragen

Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.

Warum sehe ich den Wert in regedit, aber die App sagt, er existiere nicht?
Die klassische Ursache ist, dass App und regedit auf unterschiedliche Registry-Ansichten schauen. Unter 64-Bit-Windows ist regedit ein 64-Bit-Prozess und zeigt Dinge relativ zur 64-Bit-Ansicht an, einschließlich Wow6432Node (der physischen Position der 32-Bit-Ansicht). Öffnet eine 32-Bit-App hingegen HKLM\Software, lenkt der WOW64-Registry-Umleiter sie zur Wow6432Node-Seite, sodass ein Wert, der nur in der 64-Bit-Ansicht existiert, aus Sicht der App „nicht da“ ist. Prüfen Sie zunächst, ob der Pfad des in regedit sichtbaren Werts Wow6432Node enthält, und vergleichen Sie dann beide Ansichten mit reg query über /reg:64 und /reg:32 — das führt am schnellsten zur Antwort.
Ist es zulässig, den Wow6432Node-Pfad direkt aus dem Code anzusteuern?
Das sollten Sie vermeiden. Microsofts offizielle Dokumentation stellt ausdrücklich fest, dass die physische Position des Umleitungsziels vom System reserviert ist, sich künftig ändern kann und Anwendungen nicht direkt darauf zugreifen sollten. Tatsächlich verwendet Windows 10 on ARM für 32-Bit-ARM-Apps eine andere physische Position, WowAA32Node, sodass das Festcodieren von Wow6432Node auf ARM bricht. Müssen Sie auf die andere Ansicht zugreifen, verwenden Sie die offiziellen Mechanismen: die Flags KEY_WOW64_64KEY/KEY_WOW64_32KEY oder RegistryView in .NET.
Warum liegt der Wert, den ich angeblich nach HKLM geschrieben habe, im VirtualStore unter HKCU?
Das ist die UAC-Registry-Virtualisierung am Werk. Schreibt ein 32-Bit-interaktiver Prozess ohne Schreibberechtigung unter HKLM\Software, ohne dass requestedExecutionLevel in seinem Manifest steht, wird der Schreibvorgang statt eines Fehlschlags in einen benutzerbezogenen virtuellen Speicher umgeleitet (HKEY_USERS\<SID>_Classes\VirtualStore\Machine\Software, der in regedit unter HKCU\Software\Classes\VirtualStore erscheint). Weil derselbe Prozess beim Zurücklesen eine zusammengeführte Ansicht des virtuellen Speichers und des ursprünglichen Speicherorts sieht, scheint alles zu funktionieren — doch 64-Bit-Prozesse und Dienste können den Wert nicht sehen, was zu dem merkwürdigen Fehlerbild führt, dass „die Einstellungen von Benutzer zu Benutzer unterschiedlich sind“. Virtualisierung ist eine Übergangstechnologie zur Rettung von Legacy-Apps, daher darf eine neue Anwendung sich niemals darauf verlassen.
Wie mache ich die Registry-Bitness (32-Bit/64-Bit-Ansicht) in C# explizit?
Übergeben Sie ein RegistryView an RegistryKey.OpenBaseKey. Geben Sie RegistryView.Registry64 an, können Sie die 64-Bit-Ansicht selbst aus einem 32-Bit-Prozess lesen und schreiben; geben Sie RegistryView.Registry32 an, können Sie die 32-Bit-Ansicht (die Wow6432Node-Seite) selbst aus einem 64-Bit-Prozess lesen und schreiben. RegistryView.Default überlässt es der Prozess-Bitness, daher ist es in Konfigurationen, bei denen die Bitness mit der Laufzeitumgebung variiert — etwa bei einem AnyCPU-Build —, sicherer, explizit anzugeben, welche Ansicht Sie lesen. Beachten Sie, dass eine Anforderung von Registry64 auf einem 32-Bit-Betriebssystem konstruktionsbedingt die 32-Bit-Ansicht zurückgibt, sodass derselbe Code weiterhin funktioniert, falls noch 32-Bit-Betriebssystemunterstützung besteht.
Wie deaktiviere ich die Registry-Virtualisierung, oder prüfe, ob sie aktiv ist?
Auf Seiten der Anwendung deaktiviert die Angabe eines requestedExecutionLevel im Manifest (asInvoker genügt) die Datei- und Registry-Virtualisierung für diesen Prozess. Auf administrativer Seite lässt sich mit dem Befehl reg flags das Flag REG_KEY_DONT_VIRTUALIZE pro Schlüssel setzen und abfragen. Untersuchen Sie eine bereits laufende App, ist der schnelle Weg, den Virtualisierungsstatus pro Prozess über die Spalte „UAC-Virtualisierung“ im Task-Manager zu prüfen und zu sehen, ob sich umgeleitete Werte unter HKCU\Software\Classes\VirtualStore angesammelt haben. Als dauerhafte Lösung empfehlen wir, das Design so zu korrigieren, dass von vornherein nicht nach HKLM geschrieben wird (benutzerbezogene Einstellungen gehören nach HKCU oder AppData).

Autorenprofil

Profilseite des Artikelautors.

Go Komura

Geschäftsführer von KomuraSoft LLC

Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.

Zurück zum Blog