レジストリの32bit/64bitリダイレクトと仮想化の落とし穴 ── Wow6432Nodeと「書いたはずの値がない」問題
· 更新日: · 小村 豪 · レジストリ, WOW64, Wow6432Node, UAC, 64bit移行, Windows, C#, トラブルシューティング
更新履歴(3件・最終更新 2026年08月02日)
この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。
- 記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
- 外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
- bitnessとWOW64のフルスペルを導入部で説明し、2つの仕組みの全体像の図を追加しました(リダイレクトはbitnessに関する話で読み書きの両方に効き、仮想化は権限に関する話で書き込みだけに効く、という対比です)。`reg flags`の出力と3つのフラグの意味を表にし、タスクマネージャーの「UAC仮想化」列を出す操作パスを明記しました。
- 初版公開
この記事を引用する(DOI: 10.5281/zenodo.21547421)
この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。
小村 豪(2026)「レジストリの32bit/64bitリダイレクトと仮想化の落とし穴 ── Wow6432Nodeと「書いたはずの値がない」問題」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21547421 https://staging.comcomponent.com/blog/windows-registry-redirection-virtualization/
- DOI(最新版)
- 10.5281/zenodo.21547421
- DOI(この版)
- 10.5281/zenodo.21733133
「インストーラーが書いたライセンスキーを、アプリが起動時に読めない。regeditで見ると確かに値はある」──装置連携ソフトの64bit移行を支援していたとき、こういう報告を受けました。調べてみると、値があったのは HKLM\Software\Wow6432Node\会社名 の下。新しく64bitでビルドし直したアプリは HKLM\Software\会社名 を読みに行き、そこには何もなかった、というオチです。
この手の「書いたはずの値がない」「regeditでは見えるのにアプリから見えない」は、2つの仕組みを知らないと、いくら目視で確認しても謎のままです。1つは、64bit Windows上のレジストリがプロセスのbitness(そのプロセスが32bitとして動いているか64bitとして動いているかの区別)によって別の場所に見えること ── WOW64(Windows 32-bit on Windows 64-bit、64bit Windowsが32bitアプリを動かすためのサブシステム)によるレジストリリダイレクトです。もう1つは、権限不足の書き込みが別の場所へ黙って転送されること ── UACのレジストリ仮想化です。しかもどちらも「エラーにならずに成功する」ため、問題が出るのは書いた場所と読む場所が食い違った瞬間、つまり64bit移行やインストーラー変更のタイミングです。
この記事では、Windows業務アプリの開発者を対象に、WOW64レジストリリダイレクトの仕組みとリダイレクト対象キー・共有キーの整理、UACレジストリ仮想化の発動条件、インストーラー・COM登録で起きる実害、そしてC#/C++/reg.exeでビューを明示する正しい書き方までを、公式ドキュメントの裏付けつきで整理します。
1. まず結論
- 64bit Windowsのレジストリには64bitビューと32bitビューがあり、32bitプロセスの
HKLM\Softwareへのアクセスはレジストリリダイレクターによって物理位置HKLM\Software\Wow6432Nodeへ透過的に誘導されます。アプリからはエラーも警告も見えません。1 - Wow6432Nodeという物理位置はシステムの予約領域です。パスを決め打ちして直接アクセスしてはいけません(Windows 10 on ARMでは32bit ARM用に
WowAA32Nodeという別の場所が使われます)。別ビューへは公式の手段(後述のフラグやRegistryView)でアクセスします。12 - リダイレクトされるのは一部のキーだけで、共有されるキーもあります。Windows 7以降では
HKLM\SOFTWAREは「リダイレクト」、HKLM\SOFTWARE\Classesは「共有」、ただしその下のCLSIDやInterfaceは「リダイレクト」という入れ子構造です。3 - 別ビューへのアクセスは、Win32では
RegOpenKeyExなどのsamDesiredにKEY_WOW64_64KEY/KEY_WOW64_32KEYを指定、.NETではRegistryKey.OpenBaseKeyにRegistryView.Registry64/Registry32を指定、コマンドラインではreg.exeの/reg:64//reg:32です。245 - 管理者権限のない32bit対話型プロセスが
HKLM\Softwareへ書き込むと、UACレジストリ仮想化によりHKEY_USERS\<SID>_Classes\VirtualStore\Machine\Softwareへ転送されることがあります。書き込みは「成功」し、本人が読み返すとマージビューで見えてしまうのが罠です。6 - マニフェストに
requestedExecutionLevelを指定するとファイル/レジストリ仮想化は無効になります。逆に言うと、マニフェストのないレガシーEXEだけが仮想化の対象です。7 - レジストリ仮想化は暫定的な互換技術で、Microsoftは将来のWindowsから削除する意向を明記しています。新規アプリで依存してはいけません。設計としては「HKLMに書かない」が正解です。6
- COMのCLSID登録(
HKCR\CLSID=HKLM\Software\Classes\CLSID)はbitnessごとに分かれます。32bit COMサーバーの登録は64bitクライアントから見えず、「クラスが登録されていません(0x80040154)」の定番原因になります。3
2つの仕組みの全体像
この記事で扱う2つの仕組みは、誰が・何を・どこへ転送するかが別物です。リダイレクトは「bitnessの話」で読み書きの両方に効き、仮想化は「権限の話」で書き込みだけに効きます。混同しやすいので、詳細に入る前に全体像を1枚で押さえてください。
flowchart TD
S["プロセスが HKLM の Software 配下を操作する"] --> B{"プロセスは32bitか"}
B -- "32bit" --> R["WOW64レジストリリダイレクト<br/>bitnessの話・読み書きの両方に効く"]
B -- "64bit" --> N["リダイレクトなし<br/>素の HKLM Software を読み書きする"]
R --> RV["実際に読み書きするのは<br/>Wow6432Node 配下 = 32bitビュー"]
RV --> W{"書き込みで、そのキーへの<br/>書き込み権限がない"}
N --> W
W -- "マニフェストなしの32bit対話型プロセス" --> V["UACレジストリ仮想化<br/>権限の話・書き込みだけに効く"]
V --> VS["ユーザーごとの VirtualStore へ転送<br/>読み取りは本来の場所とのマージビュー"]
W -- "64bitプロセス・サービス・マニフェストあり" --> E["アクセス拒否で素直に失敗する"]
この図の左側の分岐(誰が32bitか)が2章・3章、右下の分岐(権限とマニフェスト)が5章の内容です。
この記事の知識マップ
64bit Windowsの32bitプロセスがHKLM\Softwareへアクセスすると、WOW64のレジストリリダイレクターが物理位置Wow6432Nodeへ透過的に誘導し、この振り分けはビットネスの話として読み書きの両方に効く。別ビューへは.NETのRegistryViewやWin32のKEY_WOW64_64KEY/32KEY、reg.exeの/reg:64・/reg:32という公式手段で明示的にアクセスすべきで、パスの直書きは将来変更され得るため避ける必要がある。管理者権限のない32bit対話型プロセスがHKLM\Softwareへ書き込むと、権限の話であるUACレジストリ仮想化によりVirtualStoreへ黙って転送され、requestedExecutionLevelを指定したプロセスはこの仮想化の対象外になる。COMのCLSID登録もビューごとに分離されるため、bitnessの不一致は0x80040154エラーの定番原因になり、実際にどこを読み書きしたかはProcmonの物理パスで確定させるのが最短である。
flowchart LR
accTitle: レジストリのリダイレクトと仮想化の知識マップ
accDescr: WOW64レジストリリダイレクターがbitnessに応じてWow6432Nodeへ読み書きを振り分けること、UACレジストリ仮想化が権限不足の書き込みをVirtualStoreへ転送すること、両者の違いとビューを明示するAPI、COM登録やインストーラーで起きる実害とProcmonによる確認手段を示す図
registry_redirector["レジストリリダイレクター"]
uac_registry_virtualization["UACレジストリ仮想化"]
wow64_subsystem["WOW64(Windows 32-bit on Windows 64-bit)"]
wow6432node["WOW6432Node"]
registryview_dotnet[".NET RegistryView列挙体"]
key_wow64_flags["KEY_WOW64_64KEY/KEY_WOW64_32KEY"]
reg_exe_view_option["reg.exeの/reg:64 /reg:32"]
virtualstore["VirtualStore"]
requested_execution_level["requestedExecutionLevel(実行レベル宣言)"]
installer_app_bitness_mismatch["インストーラーとアプリのbitness不一致"]
class_not_registered_error["0x80040154(Class not registered)"]
clsid["CLSID(Class ID)"]
regsvr32["regsvr32"]
hardcoded_registry_path["Wow6432Nodeパスの直書き"]
procmon["Process Monitor(procmon.exe)"]
wow64_subsystem -->|"利用する"| registry_redirector
registry_redirector -->|"利用する"| wow6432node
registry_redirector -->|"で構成できる"| registryview_dotnet
registry_redirector -->|"で構成できる"| key_wow64_flags
registry_redirector -->|"で構成できる"| reg_exe_view_option
uac_registry_virtualization -->|"利用する"| virtualstore
requested_execution_level -->|"防止する"| uac_registry_virtualization
virtualstore -.->|"内容を継承する"| wow6432node
registry_redirector -.->|"原因になり得る"| installer_app_bitness_mismatch
registry_redirector -.->|"原因になり得る"| class_not_registered_error
clsid -.->|"に保存される"| wow6432node
regsvr32 -->|"利用する"| registry_redirector
registryview_dotnet -->|"防止する"| hardcoded_registry_path
key_wow64_flags -->|"防止する"| hardcoded_registry_path
registryview_dotnet -.->|"軽減する"| installer_app_bitness_mismatch
registry_redirector -->|"で確認できる"| procmon
uac_registry_virtualization -->|"で確認できる"| procmon
installer_app_bitness_mismatch -->|"で確認できる"| reg_exe_view_option
class_not_registered_error -->|"で確認できる"| procmon
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全19件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle
2. WOW64のレジストリリダイレクト ── 32bitプロセスはどこを見ているのか
64bit Windowsは、32bitアプリをWOW64というサブシステム上で動かします。このときレジストリリダイレクターが、32bitプロセスと64bitプロセスにそれぞれ別々の論理ビューを見せます。両者は同じAPIで同じキー名(HKEY_LOCAL_MACHINE\Software\...)を指定しているのに、実際に読み書きされる物理位置が異なる、というのが仕組みの核心です。1
リダイレクトされたキーの物理位置が Wow6432Node です。たとえば32bitプロセスの HKEY_LOCAL_MACHINE\Software は、物理的には HKEY_LOCAL_MACHINE\Software\Wow6432Node に対応付けられます。この対応付けはアプリからは完全に透過で、32bitアプリは「32bit Windows上で動いているのと同じつもり」でレジストリを操作できます。1
誰がどこを見るのかを一覧にすると、次のようになります。
| アクセス元 | コードで指定するパス | 実際に読み書きされる物理位置 |
|---|---|---|
| 64bitプロセス | HKLM\Software\MyApp |
HKLM\Software\MyApp |
| 32bitプロセス | HKLM\Software\MyApp |
HKLM\Software\Wow6432Node\MyApp |
32bitプロセス + KEY_WOW64_64KEY(RegistryView.Registry64) |
HKLM\Software\MyApp |
HKLM\Software\MyApp |
64bitプロセス + KEY_WOW64_32KEY(RegistryView.Registry32) |
HKLM\Software\MyApp |
HKLM\Software\Wow6432Node\MyApp |
| 32bit対話型プロセス・標準権限・マニフェストなしの書き込み | HKLM\Software\MyApp |
HKCU\Software\Classes\VirtualStore\Machine\Software\Wow6432Node\MyApp(WOW64リダイレクト後に仮想化。5章参照) |
| regedit(64bitプロセス) | ─ | 64bitビュー基準で表示。Wow6432Node も物理キーとしてそのまま見える |
冒頭の失敗談はこの表そのものです。32bitインストーラーが書いた値は物理的には Wow6432Node 配下にあり、64bit化したアプリは素の HKLM\Software を読む。regeditは両方見えているので、「regeditにはある」という現場報告と「アプリからはない」という現象が矛盾なく両立します。
なお、ここで Wow6432Node のパスをコードに直書きして辻褄を合わせたくなりますが、これは公式ドキュメントが明確に禁じているアンチパターンです。リダイレクト先の物理位置はシステム予約であり、変更される可能性があるとされています。実際、Windows 10 on ARMでは32bit ARMアプリのリダイレクト先は WowAA32Node という別のキーです。12 別ビューを読みたいなら、4章の公式な手段を使ってください。
細かい話としては、WOW64は32bitアプリが書き込む %ProgramFiles% で始まるREG_SZ/REG_EXPAND_SZ文字列を %ProgramFiles(x86)% に置き換える補正も行います(大文字小文字まで一致した場合のみ)。1 また、Vista/XP時代には32bit/64bitビュー間でキーをコピーして同期する「レジストリリフレクション」がありましたが、Windows 7 / Windows Server 2008 R2で廃止されています。古い解説記事を読むときは、この前提の違いに注意してください。1
3. リダイレクトされるキー・共有されるキー
すべてのキーがリダイレクトされるわけではありません。一部のキーは両ビューで1つの物理コピーを共有します。公式ドキュメント「Registry Keys Affected by WOW64」の一覧から、業務アプリ開発で関係の深いものを抜粋します(Windows 7 / Server 2008 R2以降の列。サブキーは原則として親の挙動を継承します)。3
| キー | Windows 7以降の扱い |
|---|---|
HKLM\SOFTWARE |
リダイレクト |
HKLM\SOFTWARE\Classes |
共有 |
HKLM\SOFTWARE\Classes\CLSID |
リダイレクト |
HKLM\SOFTWARE\Classes\Interface |
リダイレクト |
HKLM\SOFTWARE\Classes\DirectShow / Media Type / MediaFoundation |
リダイレクト |
HKLM\SOFTWARE\Clients |
共有 |
HKLM\SOFTWARE\Microsoft\COM3 / EventSystem / OLE / RPC |
共有 |
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths |
共有 |
HKLM\SOFTWARE\Policies |
共有 |
HKCU\SOFTWARE |
共有 |
HKCU\SOFTWARE\Classes |
共有 |
HKCU\SOFTWARE\Classes\CLSID / Interface |
リダイレクト |
注目すべきは入れ子構造です。HKLM\SOFTWARE はリダイレクトされるのに、その下の Classes は共有に戻り、さらにその下の CLSID や Interface は再びリダイレクトされます。「Software配下は全部Wow6432Nodeに行く」という雑な理解だと、ファイル拡張子の関連付け(Classes 直下、共有)とCOMのクラス登録(Classes\CLSID、リダイレクト)の挙動の違いが説明できません。HKCUは基本的に共有なので、ユーザー単位の設定をHKCUに置いている限りbitness問題はほぼ起きない、というのも実務上の重要な帰結です。3
また、KEY_WOW64_64KEY などのフラグは共有キーには効果がありません。共有キーはもともと1つしかないので、ビューを切り替える意味がないためです。2
4. 別ビューを明示的に読む ── reg.exe・C#・C++の正しい書き方
「どちらのビューに値があるのか」を確かめる最速の方法は、reg.exeの /reg:64 / /reg:32 オプションです。それぞれ64bitビュー・32bitビューを明示してアクセスします。5
:: 64bitビュー(素のHKLM\Software)を読む
reg query "HKLM\SOFTWARE\KomuraSoft\DeviceLink" /v LicenseKey /reg:64
:: 32bitビュー(物理的にはWow6432Node配下)を読む
reg query "HKLM\SOFTWARE\KomuraSoft\DeviceLink" /v LicenseKey /reg:32
この2行を実行して片方にしか値がなければ、「書いた側と読む側のbitnessが食い違っている」ことが確定します。パスに Wow6432Node を手書きするのではなく、同じ論理パスに対してビューだけを切り替えるのがポイントです。
C#(.NET)では RegistryKey.OpenBaseKey に RegistryView を渡します。Registry64(値256)、Registry32(値512)、Default(値0)の3つがあり、OpenBaseKey / OpenRemoteBaseKey / FromHandle で指定できます。4
using Microsoft.Win32;
// 32bitプロセスからでも64bitビューを読む
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
using (var key = baseKey.OpenSubKey(@"SOFTWARE\KomuraSoft\DeviceLink"))
{
var license = key?.GetValue("LicenseKey") as string;
}
// 64bitプロセスから32bitビュー(Wow6432Node側)を読む
// ── 32bit時代のインストーラーが書いた値の移行読み取りなどに使う
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 はプロセスのbitness任せです。AnyCPUビルドの.NETアプリは実行環境によって32bit/64bitが変わるため、HKLM配下の機械共通データを扱うなら、どちらのビューを読むのかをコードで明示しておくと、ビルド設定の変更(Prefer 32-bitの切り替えや64bit移行)でレジストリの見え方が突然変わる事故を防げます。なお、32bit OS上で Registry64 を要求した場合は32bitビューのキーが返る仕様なので、32bit OSサポートが残っていても同じコードで安全に動きます。4
C++(Win32 API)では、RegOpenKeyEx / RegCreateKeyEx / RegDeleteKeyEx の samDesired に KEY_WOW64_64KEY(0x0100)または KEY_WOW64_32KEY(0x0200)をORで指定します。両方同時に指定すると ERROR_INVALID_PARAMETER で失敗します。2
HKEY hKey = nullptr;
// 32bitプロセスから64bitビューのキーを開く
LSTATUS st = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\KomuraSoft\\DeviceLink",
0,
KEY_READ | KEY_WOW64_64KEY, // ここでビューを明示
&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);
}
公式ドキュメントが挙げる注意点が2つあります。いったんフラグ付きで別ビューを開いたら、その配下の子キー操作(作成・削除・オープン)にも同じフラグを明示し続けること。混在させると予期しない動作になります。また、両ビューのキーを漏れなく列挙したい場合は、KEY_WOW64_64KEY で開いたハンドルと KEY_WOW64_32KEY で開いたハンドルの2パスで列挙する必要があります。RegDeleteKey(Exなし)は別ビューにアクセスできない点も要注意です。2
5. UACレジストリ仮想化 ── HKLMに書いたはずがVirtualStoreにある
リダイレクトと混同されやすいもう1つの仕組みが、UACのレジストリ仮想化です。これはbitnessの話ではなく権限の話で、Vista以降、管理者権限を前提に書かれたレガシーアプリを救済するための互換技術です。6
動きはこうです。書き込み権限のないプロセスが HKLM\Software 配下へ値の書き込みやサブキー作成を試みると、アクセス拒否で失敗させる代わりに、書き込みがユーザーごとの仮想ストア HKEY_USERS\<ユーザーSID>_Classes\VirtualStore\Machine\Software へ転送されます(regeditでは HKCU\Software\Classes\VirtualStore\Machine\Software 配下として見えます)。さらに読み取り時には、仮想ストアの値と本来のグローバルストアの値のマージビューが返され、同名の値は仮想ストア側が優先されます。6 なお64bit OS上の32bitプロセスでは3章のWOW64リダイレクトが先に効くため、HKLM\Software\MyApp への書き込みの実際の転送先は VirtualStore\Machine\Software\Wow6432Node\MyApp になります。VirtualStore配下を調査するときは、素の Software 側だけでなく Wow6432Node 側も必ず確認してください。
つまり、書いた本人のプロセスからは何事もなかったかのように読み書きできてしまう。これが「開発機(管理者で実行)では問題なく、客先の標準ユーザー環境でだけ設定がおかしい」「Aさんのログオンでは動くのにBさんでは初期値に戻る」という、ユーザー依存の奇妙な障害の正体です。仮想ストアはユーザープロファイル(NTUSER.DATなど)の一部なので、ユーザーごとに別の内容になります(プロファイルの構造は「Windowsユーザープロファイル入門 - AppDataとNTUSER.DAT」を参照してください)。
仮想化が働く条件は限定的で、公式ドキュメントに明記されています。6
- 対象になるのは、32bitの対話型プロセスによる、
HKLM\Software配下の、管理者なら書き込めるキーへの操作だけ - 次のものは対象外: 64bitプロセス、サービスなどの非対話型プロセス、ユーザーを偽装(impersonation)中の操作、ドライバー、そしてマニフェストに
requestedExecutionLevelを指定したプロセス HKLM\Software\Classes、HKLM\Software\Microsoft\Windows、HKLM\Software\Microsoft\Windows NT配下も対象外
実務上とくに効くのが「マニフェストの有無で挙動が変わる」という点です。Visual StudioのC++リンカーは既定で asInvoker のUACフラグメントをマニフェストに埋め込むため7、今どきのツールチェーンでビルドしたEXEは最初から仮想化の対象外です。仮想化に出会うのは、マニフェストを持たないVB6/古いDelphi/古いVC++製のレガシーEXEを64bit Windowsで動かしている現場、ということになります。逆に、レガシーEXEに「とりあえずマニフェストを追加」「ビルドし直して64bit化」した途端に仮想化が切れて、今度は本当にアクセス拒否で落ちる(あるいは黙って書き込みに失敗する)という移行時の罠もあります。requestedExecutionLevel の3値(asInvoker / highestAvailable / requireAdministrator)と権限設計の考え方は「Windows の管理者特権が必要になるのはいつなのか」で詳しく扱っています。
強調しておきたいのは、公式ドキュメントが仮想化は暫定的(interim)な互換技術であり、将来のWindowsから削除する意向と明記していることです。新規開発でこの挙動に依存するのは論外で、設計原則は「アプリは機微なシステム領域(HKLM)に書かない。データはユーザー単位の場所か、適切なACLを付けた共通の場所に置く」です。6 どこに何を保存すべきかの判断は「Windowsアプリのデータ保存先の選び方」に整理しています。
なお、キー単位で仮想化を制御するフラグ(REG_KEY_DONT_VIRTUALIZE / REG_KEY_DONT_SILENT_FAIL / REG_KEY_RECURSE_FLAG)もあり、reg.exeの flags オプションで照会・設定できます。公式ドキュメントに載っている照会の例と、その出力は次のとおりです。6
C:\>reg flags HKLM\Software\AppKey1 QUERY
HKEY_LOCAL_MACHINE\Software\AppKey1
REG_KEY_DONT_VIRTUALIZE: CLEAR
REG_KEY_DONT_SILENT_FAIL: CLEAR
REG_KEY_RECURSE_FLAG: CLEAR
The operation completed successfully.
3つとも CLEAR は「フラグが立っていない」= このキーでは仮想化が既定どおり働く、という意味です。各フラグを立てた(SET)ときの効果は次のとおりです。6
| フラグ | 立てたときの効果 |
|---|---|
REG_KEY_DONT_VIRTUALIZE |
書き込みの仮想化を無効化。権限不足のキー作成・値設定はVirtualStoreへ転送されず、そのまま失敗します |
REG_KEY_DONT_SILENT_FAIL |
オープンの仮想化を無効化。権限不足のオープンを MAXIMUM_ALLOWED で開き直す救済をやめ、失敗させます |
REG_KEY_RECURSE_FLAG |
仮想化フラグを親キーから子へ伝播させます。効くのは変更後に作られる子キーだけで、既存の子キーには適用されません |
つまり「このキーだけ仮想化を止めて、権限不足を表面化させたい」なら REG_KEY_DONT_VIRTUALIZE を立てる、という使い方になります。フラグを立てる側の書式は QUERY の代わりに SET を使いますが、正確なオプションの並びは reg flags /? で確認してください。
トラブル調査では、タスクマネージャーの「詳細」タブで列見出しを右クリックし、「列の選択」から「UAC 仮想化」を追加してプロセス単位の仮想化状態を見る、HKCU\Software\Classes\VirtualStore 配下を覗いて転送された残骸がないか見る、の2つが手早い確認手段です。
6. インストーラーとCOM登録で起きる実害
この2つの仕組みが実害として噴き出しやすいのが、インストーラーとCOM登録です。
インストーラーのbitness問題。32bitのインストーラー(32bit MSIや32bitのセットアップEXE)が HKLM\Software\会社名 に書いた設定は、物理的には Wow6432Node 配下に入ります。アプリ本体を64bit化したのにインストーラーを32bitのまま流用すると、冒頭の失敗談のとおり「インストーラーは書いた、アプリは読めない」が完成します。逆パターン(64bitインストーラー+32bitアプリ)も同様です。インストーラーとアプリ本体で「どのビューに書き、どのビューを読むか」を揃えること、64bit移行の過渡期には4章の RegistryView.Registry32 で旧位置からの移行読み取りを実装することが対策になります。
COM登録のbitness問題。3章の表のとおり、HKLM\Software\Classes\CLSID(= HKCR\CLSID のHKLM側)はリダイレクト対象です。つまり32bit COMサーバーのCLSID登録は32bitビューに、64bitの登録は64bitビューに入り、互いに見えません。3 64bitプロセスから32bit DLLのインプロセスCOMサーバーは元々ロードできないので、この分離自体は合理的なのですが、現場では「regsvr32で登録したのに、クライアントからは0x80040154(クラスが登録されていません)」という形で襲ってきます。regsvr32自体にも32bit版(SysWOW64側)と64bit版(System32側)があり、どちらで登録したかで書き込まれるビューが決まります。COM登録とbitnessの組み合わせで起きる罠の全体像は「COM/OCX/ActiveX開発でハマる登録とbitnessの罠」に、レジストリ登録自体を不要にする選択肢は「Reg-Free COMとは」にまとめています。
COMの世界にはもう1つ歴史的事情があります。Vista/XP時代は CLSID などが「リダイレクト+リフレクション(両ビュー間の同期)」で扱われていましたが、Windows 7でリフレクションが廃止され、現在は純粋にビューごとの分離になりました。3 また、HKLM\SOFTWARE\Wow6432Node\Classes → HKLM\SOFTWARE\Classes\Wow6432Node のような互換用シンボリックリンクがいくつか定義されていますが、これはWow6432Nodeをハードコードしてしまった既存アプリの救済用であり、新規アプリが使ってよいものではありません。3
なお、レジストリの分離を乗り越えてもDLL自体のロードには別の名前解決ルールが待っています。「見つからない」系の調査では「Windows DLL名前解決の仕組み - 検索順序とSxS」もあわせてどうぞ。
7. トラブルシューティング ── Procmonで「実際にどこを読んだか」を見る
仕組みを知っていても、目の前の障害で「このプロセスは結局どの物理キーを読んだのか」を確定させるには観測が必要です。ここで最強なのがSysinternalsのProcess Monitor(Procmon)です。
手順はシンプルです。
- Procmonを起動し、フィルターで
Process Name is <対象アプリ>.exeを追加 - ツールバーでレジストリ操作のみ表示に絞る(
Operation begins with Regのフィルターでも可) - アプリの問題の操作を再現し、
RegOpenKey/RegQueryValue/RegSetValueの行を見る
ポイントは、ProcmonのPathカラムにはリダイレクト解決後の物理パスが表示されることです。32bitアプリが HKLM\Software\MyApp を開いたつもりでも、Procmon上は HKLM\SOFTWARE\WOW6432Node\MyApp と出ます。ここに NAME NOT FOUND が並んでいれば「どのビューの、どのキーがないのか」が一目瞭然ですし、書き込みが HKCU\Software\Classes\VirtualStore\... に流れていれば仮想化の発動も観測できます。COMの 0x80040154 調査なら、CLSID\{...} のオープン失敗がどちらのビューで起きたかまで追えます。Procmonのフィルター設計や読み方の詳細は「Process Monitor(ProcMon)実践ガイド」を参照してください。
8. 実務の定石(判断表)
| 状況 | やること | 理由・補足 |
|---|---|---|
| 「regeditでは見えるのにアプリから見えない」 | reg query ... /reg:64 と /reg:32 で両ビューを読み比べ |
どちらのビューに値があるかを最初に確定させる5 |
| 32bit/64bit両方の自社プロセスが同じHKLM設定を読む | 書き込み側でビューを固定し(例: 64bitビュー)、読む側全員が同じビューを明示 | RegistryView.Registry64 / KEY_WOW64_64KEY で統一42 |
コードに Wow6432Node を直書きしたくなった |
書かない。ビュー指定APIに置き換える | 物理位置はシステム予約。ARMでは WowAA32Node になる12 |
| ユーザー単位の設定の置き場所 | HKCU(またはAppData)に置く | HKCUは共有キーでbitness問題がなく、権限問題もない3 |
| レガシー32bitアプリの設定が「ユーザーによって違う」 | HKCU\Software\Classes\VirtualStore を確認 |
仮想化で転送された値がユーザーごとに溜まっている典型パターン6 |
| レガシーEXEにマニフェスト追加/64bit化する | HKLM書き込み箇所を洗い出してから実施 | 仮想化が無効になり、今まで「動いていた」書き込みが失敗し始める67 |
| COMの0x80040154 | クライアントとサーバーのbitnessを確認し、対応するregsvr32/ビューで登録を確認 | CLSID登録はビューごとに分離されている3 |
| どこを読んだか確定できない | Procmonで物理パスと結果(NAME NOT FOUNDなど)を観測 | 推測をやめて事実を見るのが最短 |
9. まとめ
- 64bit Windowsのレジストリはビューが2つあり、32bitプロセスの
HKLM\SoftwareはWow6432Nodeへ透過的にリダイレクトされます。「書いたはずの値がない」の第一容疑者です。 - リダイレクトは全キーではありません。
Classesは共有、その下のCLSID/Interfaceはリダイレクト、HKCUはほぼ共有という入れ子構造を押さえてください。 - Wow6432Nodeの直書きは禁物です。reg.exeの
/reg:64/reg:32、.NETのRegistryView、Win32のKEY_WOW64_64KEY/KEY_WOW64_32KEYでビューを明示します。 - UACレジストリ仮想化は、マニフェストのない32bit対話型プロセスの権限不足のHKLM書き込みをVirtualStoreへ黙って転送します。レガシー救済の暫定技術であり、新規アプリで依存してはいけません。
- インストーラーとアプリ、COMサーバーとクライアントは「どのビューを使うか」を揃える。64bit移行時は旧ビューからの移行読み取りを設計に入れます。
- 迷ったらProcmonで物理パスを観測する。推測より観測が早く、確実です。
関連記事
- COM/OCX/ActiveX開発でハマる登録とbitnessの罠
- Reg-Free COMとは
- Windows の管理者特権が必要になるのはいつなのか
- Windowsユーザープロファイル入門 - AppDataとNTUSER.DAT
- Windowsアプリのデータ保存先の選び方
- Process Monitor(ProcMon)実践ガイド
- Windows DLL名前解決の仕組み - 検索順序とSxS
関連する相談領域
合同会社小村ソフトでは、「レジストリに書いたはずの値が読めない」「COM登録が見つからない」といった不具合の調査、32bitアプリ・COMコンポーネント資産の64bit移行設計、装置連携ソフトを含むWindows業務アプリの受託開発を扱っています。
参考リンク
-
Microsoft Learn, Registry Redirector. レジストリリダイレクターが32bit/64bitアプリに別々の論理ビューを提供しアプリに透過であること、HKEY_LOCAL_MACHINE\SoftwareがHKEY_LOCAL_MACHINE\Software\Wow6432Nodeへリダイレクトされること、物理位置はシステム予約でありアプリが直接アクセスすべきでないこと、Windows 10 on ARMの32bit ARMキーはWowAA32Nodeへマップされること、%ProgramFiles%文字列の置換、リフレクションがWindows 7 / Windows Server 2008 R2で廃止されたことについて。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Microsoft Learn, Accessing an Alternate Registry View. KEY_WOW64_64KEY(0x0100)とKEY_WOW64_32KEY(0x0200)の意味、RegCreateKeyEx・RegDeleteKeyEx・RegOpenKeyExのsamDesiredで指定すること、両フラグ同時指定はERROR_INVALID_PARAMETERになること、共有キーには効果がないこと、子キー操作で同じフラグを使い続けるべきこと、全キー列挙は2パスで行うこと、Wow6432Node/WowAA32Nodeが予約キーであることについて。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Microsoft Learn, Registry Keys Affected by WOW64. リダイレクトされるキーと共有されるキーの一覧(Windows 7以降でHKLM\SOFTWAREはリダイレクト、HKLM\SOFTWARE\Classesは共有、Classes\CLSID・Interface・DirectShow等はリダイレクト、Clients・COM3・OLE・RPC・App Paths・Policies・HKCU\SOFTWARE等は共有)、サブキーが親の挙動を継承すること、HKCR がHKLMとHKCUのClassesのマージビューであること、Wow6432Nodeを含む互換用シンボリックリンクは既存アプリの救済用で新規アプリは使うべきでないことについて。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
-
Microsoft Learn, RegistryView Enum (Microsoft.Win32). RegistryView列挙体のDefault(0)・Registry64(256)・Registry32(512)、OpenBaseKey・OpenRemoteBaseKey・FromHandleでビューを指定できること、32bit OSで64bitビューを要求した場合は32bitビューのキーが返ることについて。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, reg query. reg queryコマンドの/reg:32が32bitレジストリビューで、/reg:64が64bitレジストリビューでキーへアクセスするオプションであることについて。 ↩ ↩2 ↩3
-
Microsoft Learn, Registry Virtualization. HKLM\Softwareへの書き込みがHKEY_USERS<User SID>_Classes\VirtualStore\Machine\Softwareへリダイレクトされること、読み取り時に仮想ストア優先のマージビューが返ること、仮想化の対象が32bit対話型プロセスかつHKLM\Software配下かつ管理者が書き込めるキーに限られること、64bitプロセス・サービス・偽装中の操作・requestedExecutionLevel指定プロセス・Classes等のサブキーでは無効なこと、暫定的な互換技術で将来削除の意向でありアプリが依存すべきでないこと、reg flagsによるREG_KEY_DONT_VIRTUALIZE等の制御について。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10
-
Microsoft Learn, Application manifests. requestedExecutionLevel要素のasInvoker・requireAdministrator・highestAvailableの意味、requestedExecutionLevelノードを指定するとファイルおよびレジストリの仮想化が無効になること、後方互換のために仮想化を利用したい場合はこのノードを省略すること、Visual C++リンカーが既定でasInvokerのUACフラグメントをマニフェストに埋め込むことについて。 ↩ ↩2 ↩3
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
Windowsアプリのデータ保存先の選び方 ── SQLite / JSON / レジストリ / Access 判断表
Windowsデスクトップアプリのデータをどこに・何で保存するか。AppData/ProgramDataの使い分け、SQLite・JSONファイル・レジストリ・Access(.accdb)それぞれの得意分野と落とし穴を判断表つきで整理し、破損対策やビット数問題まで実務目線で...
マルチスレッドの実務ベストプラクティス .NET編 ── スレッドを増やす前に決めておくこと
「スレッドを立てたら、たまに落ちる・固まる」を防ぐ設計の定石を.NET/C#向けに整理。スレッドを自分で作らずTaskに乗る、共有可変状態を減らす、ロックの規律、CancellationTokenでの停止設計、UIスレッドの扱いまで解説します。
WMI/CIMをC#・PowerShellから使う ── ハードウェア情報取得・プロセス監視・リモート照会の実務ガイド
PCのシリアル番号取得、ディスク空き監視、プロセス起動検知の定番がWMI/CIMです。Get-CimInstance等のCIMコマンドレットの使い方と旧Get-WmiObjectからの移行、C#のSystem.ManagementとCIM APIの使い分け、実例レシピと落と...
MSMQはいつまで使えるのか ── 「非推奨ですらない」レガシーキューの移行判断
MSMQは公式の非推奨リストに載っていない一方、System.Messagingは.NET Frameworkにしか存在せず、.NETへの移行を妨げます。廃止の噂と実際の現在地を事実で整理し、使い続ける・移行するの判断基準と移行先の選び方をまとめます。
WindowsのTPMとは何か ── 図解でわかる「鍵を外に出さない金庫」と測定起動
TPMを図解で解説します。鍵をチップの外へ出さない仕組み、PCRと測定起動、BitLockerやWindows Helloでの使われ方、dTPM・fTPM・Plutonの違い、Get-Tpmでの確認方法、回復キーを求められたときの対処までを実務目線で整理します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
業務アプリ、装置連携、通信ツールなどの Windows ソフト開発を支援します。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- regeditでは値が見えるのに、アプリから読むと存在しないのはなぜですか?
- アプリとregeditが見ているレジストリビューが違うのが典型原因です。64bit Windowsのregeditは64bitプロセスで、64bitビューを基準にWow6432Node(32bitビューの物理位置)も含めて表示します。一方、32bitアプリがHKLM\Softwareを開くとWOW64のレジストリリダイレクターによりWow6432Node側へ誘導されるため、64bitビュー側にしかない値は「存在しない」ことになります。regeditで見えている値のパスにWow6432Nodeが含まれているかどうかをまず確認し、reg query の /reg:64 と /reg:32 で両ビューを読み比べると切り分けが早いです。
- コードからWow6432Nodeのパスを直接指定してアクセスしてもよいですか?
- 避けるべきです。Microsoftの公式ドキュメントは、リダイレクト先の物理位置はシステムの予約領域であり、将来変更される可能性があるためアプリケーションが直接アクセスすべきではないと明記しています。実際、Windows 10 on ARMでは32bit ARMアプリ用にWowAA32Nodeという別の物理位置が使われており、Wow6432Nodeの決め打ちはARM環境で破綻します。別ビューへアクセスしたい場合は、KEY_WOW64_64KEY/KEY_WOW64_32KEYフラグや.NETのRegistryViewという公式の手段を使ってください。
- HKLMに書いたはずの値がHKCUのVirtualStoreにあるのはなぜですか?
- UACのレジストリ仮想化が働いた結果です。書き込み権限のない32bitの対話型プロセスが、マニフェストにrequestedExecutionLevelを持たないままHKLM\Software配下へ書き込むと、失敗する代わりにユーザーごとの仮想ストア(HKEY_USERS\<SID>_Classes\VirtualStore\Machine\Software、regeditではHKCU\Software\Classes\VirtualStore配下に見えます)へ転送されます。そのプロセスが読み返すと仮想ストアと本来の場所のマージビューが見えるため一見動いてしまいますが、64bitプロセスやサービスからはその値が見えず、「ユーザーによって設定が違う」奇妙な障害になります。仮想化はレガシー救済の暫定技術なので、新規アプリで依存してはいけません。
- C#でレジストリのbitness(32bit/64bitビュー)を明示するにはどうしますか?
- RegistryKey.OpenBaseKeyにRegistryViewを渡します。RegistryView.Registry64を指定すれば32bitプロセスからでも64bitビューを、RegistryView.Registry32を指定すれば64bitプロセスからでも32bitビュー(Wow6432Node側)を読み書きできます。RegistryView.Defaultはプロセスのbitness任せなので、AnyCPUビルドのように実行環境でbitnessが変わる構成では、どちらのビューを読むかを明示するのが安全です。なお32bit OSでRegistry64を要求した場合は32bitビューが返る仕様なので、32bit OS対応が残る場合も同じコードで動きます。
- レジストリ仮想化を無効にする、または仮想化されているか確認するにはどうしますか?
- アプリ側では、マニフェストにrequestedExecutionLevel(asInvokerでも可)を指定すれば、そのプロセスのファイル/レジストリ仮想化は無効になります。管理者側では、reg flagsコマンドでキー単位のREG_KEY_DONT_VIRTUALIZEフラグを設定・確認できます。すでに動いているアプリの調査なら、タスクマネージャーの「UAC仮想化」列でプロセス単位の仮想化状態を確認し、HKCU\Software\Classes\VirtualStore配下に転送された値が溜まっていないかを見るのが早道です。恒久対策としては、そもそもHKLMに書かない設計(ユーザー単位の設定はHKCUやAppDataへ)に直すことをおすすめします。