更新履歴(3件・最終更新 2026年08月02日)
この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。
- 記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
- 外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
- bitnessとWOW64のフルスペルを導入部で説明し、2つの仕組みの全体像の図を追加しました(リダイレクトはbitnessに関する話で読み書きの両方に効き、仮想化は権限に関する話で書き込みだけに効く、という対比です)。`reg flags`の出力と3つのフラグの意味を表にし、タスクマネージャーの「UAC仮想化」列を出す操作パスを明記しました。
- 初版公開
この記事を引用する(DOI(登録済みアーカイブ): 10.5281/zenodo.21547421)
以下のDOIは過去に登録されたアーカイブを指しており、現在の本文とは一致しない場合があります。現在の本文を参照するときは、このページのURLを使用してください。
小村 豪(2026)「レジストリの32bit/64bitリダイレクトと仮想化の落とし穴 ── Wow6432Nodeと「書いたはずの値がない」問題」合同会社小村ソフト. https://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\会社名 を読んでいました。値が消えたのではなく、書いた側と読む側が別の場所を見ていたのです。
この問題では、まず2つの仕組みを分けます。WOW64のレジストリリダイレクトは、32bit/64bitのビューを振り分ける仕組み。UACのレジストリ仮想化は、レガシーアプリの権限不足の書き込みを、ユーザーごとの場所へ転送する仕組みです。どちらもアプリに透過的に働くため、「成功したはずなのに、別のプロセスから見えない」という症状になります。12
この記事はWindows業務アプリの開発者向けに、何を確認するか → なぜ見え方が変わるか → どう読み書きを揃えるか → 移行時に何を直すかの順に整理します。以下でいう bitness は、そのプロセスが32bitと64bitのどちらで動いているかの区別です。WOW64 はWindows 32-bit on Windows 64-bitの略で、64bit Windows上で32bitアプリを動かすためのサブシステムを指します。
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全19件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle
1. まず結論 ── パスを書き換える前に、見ている場所を確かめる
「regeditにある」だけでは、アプリが同じ値を読めるとは限りません。 ビューの違いと仮想化を切り分け、書き込み側・読み取り側が実際にどこへアクセスしたかを確認します。
| 切り分けたいこと | 最初に確認するもの | 詳しい説明 |
|---|---|---|
| 32bit/64bitで別の場所を見ていないか | 同じ論理パスを reg query の /reg:64 と /reg:32 で読み比べる |
2.1節、3章 |
| 権限不足の書き込みが転送されていないか | プロセスのUAC仮想化状態と、該当ユーザーのVirtualStore | 2.2節、5章 |
| アプリが結局どこを読んだか | Procmonでレジストリ操作のPathと結果を見る | 2.3節 |
原因が分かった後も、コードに Wow6432Node を直書きして合わせるのは避けます。別ビューへは RegistryView、KEY_WOW64_64KEY / KEY_WOW64_32KEY、reg.exe のビュー指定を使います。物理位置はシステムの予約領域だからです。13
また、仮想化を新規アプリの保存方式にしてはいけません。ユーザー設定はHKCUやAppDataへ、共通データは適切なACLを付けた場所へ置きます。HKLMに置く機械共通の設定を読むなら、インストーラーとアプリで使うビューを揃えます。2
目の前の障害を調べる場合は2章、実装を直す場合は4章、64bit化やマニフェスト追加の前に確認する場合は6〜7章へ進んでください。最後に8章の判断表で対応を確認できます。
flowchart TB
accTitle: 値がないときの調査順序
accDescr: 両ビューとユーザー別の保存先を確認し、実際のアクセスを観測してから対処を選びます。
A["同じ論理パスを照会"] --> B["32bit/64bitビューを比較"]
B --> C["仮想化状態と保存先を確認"]
C --> D["Procmonで実アクセスを確認"]
D --> E["原因に合う対処を選ぶ"]
図1: 両ビューとユーザー別の保存先を確認し、実際のアクセスを観測してから対処を選びます。
2. まず調べる ── 両ビュー・VirtualStore・実際のアクセス先
2.1 reg.exeで同じ論理パスを読み比べる
「どちらのビューに値があるか」は、reg.exe の /reg:64 / /reg:32 で確認できます。それぞれ64bitビュー・32bitビューを明示してアクセスするオプションです。以下は読み取りの例で、パスと値名は調査対象に置き換えます。4
:: 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
ポイントは、パスに Wow6432Node を足さず、同じ論理パスに対してビューだけを切り替えることです。片方にしか値がなく、アプリがもう片方を読んでいるなら、冒頭と同じ不一致です。書き込み側・読み取り側のbitnessと、明示的なビュー指定の有無を照合します。
ここで分かるのは、各ビューに値があるかどうかです。アプリが実際にどちらへアクセスしたかの確定には、2.3節の観測を使います。
flowchart TB
accTitle: 値の存在とアプリのアクセスを分ける
accDescr: 各ビューへの照会結果と、アプリが実際に読んだ場所を突き合わせます。
A["同じパスと値名"] --> B["reg query /reg:64"]
A --> C["reg query /reg:32"]
B --> D["存在するビューを確認"]
C --> D
D --> E["アプリのアクセス先と照合"]
図2: 各ビューへの照会結果と、アプリが実際に読んだ場所を突き合わせます。
2.2 ユーザーによって違うなら、仮想化状態とVirtualStoreを見る
「管理者で動かす開発機では正常なのに、標準ユーザーでは設定がおかしい」「Aさんでは動くのにBさんでは初期値に戻る」。こうした場合は、32bit/64bitの違いだけでなく、ユーザーごとの仮想ストアを確認します。
タスクマネージャーの「詳細」タブで列見出しを右クリックし、「列の選択」から「UAC 仮想化」を追加すると、プロセス単位の状態を確認できます。あわせて、該当ユーザーの HKCU\Software\Classes\VirtualStore 配下に、転送された値がないかを見ます。
64bit OS上の32bitプロセスではWOW64リダイレクトも関係するため、VirtualStoreの Machine\Software だけでなく、その下の Wow6432Node 側も確認してください。転送と読み返しの仕組み、対象外になる条件は5章で説明します。
flowchart TB
accTitle: ユーザー別の設定を調べる
accDescr: ユーザー依存の症状ではプロセスの仮想化状態と、そのユーザーのVirtualStoreを組み合わせて確認します。
A["ユーザーごとに結果が違う"] --> B["対象プロセスの状態を確認"]
B --> C["該当ユーザーのVirtualStore"]
C --> D["Software側を確認"]
C --> E["Wow6432Node側も確認"]
図3: ユーザー依存の症状ではプロセスの仮想化状態と、そのユーザーのVirtualStoreを組み合わせて確認します。
2.3 Procmonで「実際にどこを読んだか」を確定する
仕組みを知っていても、目の前のアプリがどの物理キーへアクセスしたかは観測が必要です。SysinternalsのProcess Monitor(Procmon)で、問題の操作を再現します。
- Procmonを起動し、フィルターで
Process Name is <対象アプリ>.exeを追加します。 - ツールバーでレジストリ操作だけに絞ります。
Operation begins with Regのフィルターでも構いません。 - アプリの問題の操作を再現し、
RegOpenKey/RegQueryValue/RegSetValueの行を確認します。
Pathカラムには、リダイレクト解決後の物理パスが表示されます。 32bitアプリが HKLM\Software\MyApp を指定していても、Procmonには HKLM\SOFTWARE\WOW6432Node\MyApp と出ます。そこで NAME NOT FOUND が並べば、どのビューのどのキーがないのかを追えます。書き込みが HKCU\Software\Classes\VirtualStore\... に流れていれば、仮想化の発動も観測できます。
COMの 0x80040154 を調べる場合も、CLSID\{...} のオープンがどちらのビューで失敗したかを確認します。フィルター設計と読み方の詳細は「Process Monitor(ProcMon)実践ガイド」を参照してください。
flowchart TB
accTitle: Procmonで論理パスの先を観測する
accDescr: 対象プロセスのレジストリ操作を再現し、Pathと結果から不一致を確定します。
A["対象プロセスで絞る"] --> B["レジストリ操作だけ表示"]
B --> C["問題の操作を再現"]
C --> D["Pathと結果を読む"]
D --> E["ビューと仮想化を切り分ける"]
図4: 対象プロセスのレジストリ操作を再現し、Pathと結果から不一致を確定します。
3. WOW64のレジストリリダイレクト ── 同じパスでもビューが違う
3.1 アプリが指定する論理パスと、実際の物理位置
レジストリリダイレクターは、64bit Windows上の32bitプロセスと64bitプロセスに、別々の論理ビューを見せます。同じAPIで同じキー名を指定しても、リダイレクト対象のキーでは実際に読み書きする物理位置が変わります。この対応付けはアプリに透過的で、32bitアプリは32bit Windows上と同じつもりで操作できます。1
たとえば、32bit x86プロセスの HKLM\Software\MyApp は HKLM\Software\Wow6432Node\MyApp に対応します。以下は、通常の32bit/64bitビューと、後述する仮想化が働く場合の対応をまとめた表です。共有キーと仮想化の対象外条件は、それぞれ3.2節・5章で分けて確認します。
| アクセス元 | コードで指定するパス | 実際に読み書きされる物理位置 |
|---|---|---|
| 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インストーラーが書いた値は32bitビューにあり、64bit化したアプリは64bitビューを読んでいました。一方、64bitのregeditは64bitビューを基準に、Wow6432Node も物理キーとして表示します。「regeditにはある」と「アプリからはない」は、矛盾せずに両立します。
flowchart TB
accTitle: 同じ論理パスを別のビューで開く
accDescr: リダイレクト対象のキーでは、同じ論理パスでも既定のビューがプロセスのbitnessによって異なります。
A["同じ論理パス"] --> B["32bit x86プロセス"]
A --> C["64bitプロセス"]
B --> D["既定では32bitビュー"]
C --> E["既定では64bitビュー"]
D -.-> F["書いた場所と読む場所がずれる"]
E -.-> F
図5: リダイレクト対象のキーでは、同じ論理パスでも既定のビューがプロセスのbitnessによって異なります。
3.2 リダイレクト対象と共有キーを分ける
すべてのキーがリダイレクトされるわけではありません。共有キーは、両ビューで1つの物理コピーを使います。 公式の「Registry Keys Affected by WOW64」から、業務アプリで関係の深いものを抜粋します。表はWindows 7 / Windows Server 2008 R2以降の扱いで、サブキーは原則として親の挙動を継承します。5
| キー | 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へ行く」では、ファイル拡張子の関連付けとCOM登録の違いを説明できません。5
HKCUの通常のユーザー設定は基本的に共有なので、そこに保存する設定ではbitness問題を避けやすくなります。ただし、表の HKCU\SOFTWARE\Classes\CLSID / Interface のような例外まで共有扱いにしないでください。KEY_WOW64_64KEY などのビュー指定フラグは、もともと1つしかない共有キーには効果がありません。53
flowchart TB
accTitle: 共有キーではビューの指定を変えても同じ場所を見る
accDescr: 共有キーは両ビューで1つの物理コピーを使うため、ビューを切り替えても分離されません。
A["32bitビューからアクセス"] --> C["共有キーの物理コピーは1つ"]
B["64bitビューからアクセス"] --> C
C --> D["ビュー指定で別の場所にはならない"]
図6: 共有キーは両ビューで1つの物理コピーを使うため、ビューを切り替えても分離されません。
3.3 Wow6432Nodeの直書きで直してはいけない
別の場所にあると分かると、パスに Wow6432Node を足したくなります。しかし、公式資料はリダイレクト先の物理位置をシステム予約とし、アプリが直接アクセスすべきではないとしています。将来変わる可能性があるためです。Windows 10 on ARMでは、32bit ARMアプリ用に WowAA32Node という別の位置も使われます。13
コードでは論理パスを保ち、4章のAPIでビューを指定します。物理パスは、調査で観測するものと、実装で決め打ちするものを区別してください。
flowchart TB
accTitle: 物理位置ではなくビューを指定する
accDescr: 調査で見えた予約パスをコードに埋め込まず、論理パスと公式のビュー指定を組み合わせます。
A["調査で物理パスを確認"] --> B["コードは論理パスを保持"]
B --> C["APIでビューを指定"]
C --> D["OSに物理位置の対応付けを任せる"]
図7: 調査で見えた予約パスをコードに埋め込まず、論理パスと公式のビュー指定を組み合わせます。
3.4 文字列の補正と、古いリフレクションの説明に注意する
WOW64は、32bitアプリが書き込む %ProgramFiles% で始まるREG_SZ/REG_EXPAND_SZ文字列を、一定の条件で %ProgramFiles(x86)% へ置き換える補正も行います。大文字小文字まで一致する必要がある点も含め、詳細条件は公式資料を確認してください。1
また、Vista/XP時代には、32bit/64bitビュー間でキーをコピーして同期するレジストリリフレクションがありました。これはWindows 7 / Windows Server 2008 R2で廃止されています。古い解説にある「同期される」という前提を、現在のリダイレクトと混同しないようにします。1
flowchart TB
accTitle: リダイレクトと過去のリフレクションを分ける
accDescr: ビューを分けるリダイレクトと、古いWindowsで一部キーを同期したリフレクションは別の仕組みです。
A["古い解説の同期という説明"] --> B["対象OSの世代を確認"]
B --> C["過去の一部キーは同期も実施"]
B --> D["Windows 7以降は同期を廃止"]
D --> E["共有とリダイレクトの一覧で判断"]
図8: ビューを分けるリダイレクトと、古いWindowsで一部キーを同期したリフレクションは別の仕組みです。
4. 別ビューを明示的に読む ── C#とC++の書き方
4.1 C#はRegistryKey.OpenBaseKeyでビューを指定する
.NETでは RegistryKey.OpenBaseKey に RegistryView を渡します。Registry64(256)、Registry32(512)、Default(0)の3つがあり、OpenRemoteBaseKey / FromHandle でも指定できます。6
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ビルドでは実行環境によってbitnessが変わるため、HKLMの機械共通データを扱うなら、読むビューを明示しておくと、Prefer 32-bitの変更や64bit移行で見え方が変わる事故を防げます。
32bit OSで Registry64 を要求した場合には32bitビューが返る仕様です。32bit OSのサポートが残っている場合も、このビュー指定を使えます。6
flowchart TB
accTitle: 実行環境でなくデータの置き場所に合わせる
accDescr: 機械共通の設定を読むビューを明示すると、プロセスのbitness変更に読み取り先を委ねずに済みます。
A["機械共通の設定のビューを決める"] --> B["OpenBaseKeyへRegistryViewを指定"]
B --> C["その基点からサブキーを開く"]
C --> D["書き込み側と同じビューを読む"]
図9: 機械共通の設定を読むビューを明示すると、プロセスのbitness変更に読み取り先を委ねずに済みます。
4.2 C++はsamDesiredにWOW64フラグを指定する
Win32 APIでは RegOpenKeyEx / RegCreateKeyEx / RegDeleteKeyEx の samDesired に、KEY_WOW64_64KEY(0x0100)または KEY_WOW64_32KEY(0x0200)をORで指定します。両方を同時に指定すると ERROR_INVALID_PARAMETER で失敗します。3
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);
}
4.3 子キーの操作と、両ビューの列挙にも指定を揃える
公式資料の注意点は、開いた後の操作にも及びます。別ビューをフラグ付きで開いたら、子キーの作成・削除・オープンにも同じフラグを明示し続けます。 混在させると予期しない動作になります。RegDeleteKey(Exなし)では別ビューへアクセスできない点にも注意してください。3
両ビューのキーを漏れなく列挙したい場合は、KEY_WOW64_64KEY で開いたハンドルと、KEY_WOW64_32KEY で開いたハンドルを使い、2パスで列挙します。片方を調べることと、両方を調べることは別の処理です。3
flowchart TB
accTitle: 両ビューを列挙するには2回調べる
accDescr: 両ビューのキーが必要なら、それぞれのビューを明示したハンドルで別々に列挙します。
A["両ビューのキーが必要"] --> B["64bitビューのハンドル"]
A --> C["32bitビューのハンドル"]
B --> D["64bitビューを列挙"]
C --> E["32bitビューを列挙"]
D --> F["両方の結果を確認"]
E --> F
図10: 両ビューのキーが必要なら、それぞれのビューを明示したハンドルで別々に列挙します。
5. UACレジストリ仮想化 ── 成功した書き込みがユーザー別になる
5.1 まず、仮想化の対象になる条件を確認する
UACレジストリ仮想化は、Vista以降、管理者権限を前提に作られたレガシーアプリを救済するための互換技術です。リダイレクトがビューを分ける話なのに対し、こちらは権限不足を救済する話です。ただし、すべての権限不足を救済するわけではありません。2
対象と対象外の条件は、公式資料で次のように限定されています。2
- 対象になるのは、32bitの対話型プロセスによる、
HKLM\Software配下の、管理者なら書き込めるキーへの操作です。 - 64bitプロセス、サービスなどの非対話型プロセス、ユーザーを偽装(impersonation)中の操作、ドライバー、マニフェストに
requestedExecutionLevelを指定したプロセスは対象外です。 HKLM\Software\Classes、HKLM\Software\Microsoft\Windows、HKLM\Software\Microsoft\Windows NT配下も対象外です。
つまり、「32bitだから」「マニフェストがないから」という1条件だけで、必ず転送されるとは判断できません。キー単位の制御も5.4節で確認します。
flowchart TB
accTitle: 仮想化の条件はプロセスとキーの両方で調べる
accDescr: 32bit対話型プロセスかどうかだけでなく、対象外の実行条件・キー・仮想化フラグも確認します。
A["権限不足の書き込み"] --> B["プロセスの対象条件を確認"]
B --> C["キーの対象条件を確認"]
C --> D["仮想化フラグを確認"]
D --> E["転送される条件か判断"]
図11: 32bit対話型プロセスかどうかだけでなく、対象外の実行条件・キー・仮想化フラグも確認します。
5.2 書き込みは転送され、読み返すとマージされる
対象のプロセスが書き込み権限のない HKLM\Software 配下へ値を書いたり、サブキーを作ったりすると、アクセス拒否で失敗させる代わりに、ユーザーごとの仮想ストアへ書き込まれます。場所は HKEY_USERS\<ユーザーSID>_Classes\VirtualStore\Machine\Software で、regeditでは HKCU\Software\Classes\VirtualStore\Machine\Software 配下として見えます。2
さらに、その仮想化されたキーを読むと、本来のグローバルストアと仮想ストアのマージビューが返ります。同名の値が両方にある場合は仮想ストア側が優先されるため、書いた本人のプロセスでは正常に読み書きできたように見えます。権限不足の書き込み救済が入口ですが、影響は読み返した結果にも表れます。2
flowchart TB
accTitle: 書き込み成功でもグローバル設定は変わらない
accDescr: 条件を満たす権限不足の書き込みはユーザーの仮想ストアへ転送され、アプリには成功として見えます。
A["HKLMへ書き込みを試みる"] --> B["権限不足で仮想化が働く"]
B --> C["ユーザーのVirtualStoreへ書く"]
C --> D["アプリからは成功に見える"]
D -.-> E["本来のHKLMを更新したとは限らない"]
図12: 条件を満たす権限不足の書き込みはユーザーの仮想ストアへ転送され、アプリには成功として見えます。
flowchart TB
accTitle: 仮想化された読み取りでは同名値の優先順位がある
accDescr: 仮想化されたキーの読み取りは両ストアをマージし、同名の値は仮想ストア側を優先します。
A["本来のグローバルストア"] --> C["仮想化された読み取り"]
B["ユーザーの仮想ストア"] --> C
C --> D["同名なら仮想ストアを優先"]
D --> E["本人には書いた値が見える"]
図13: 仮想化されたキーの読み取りは両ストアをマージし、同名の値は仮想ストア側を優先します。
5.3 WOW64リダイレクトと仮想化が重なる場合
64bit OS上の32bitプロセスでは、WOW64リダイレクトが先に働きます。たとえば HKLM\Software\MyApp への書き込みが仮想化されると、転送先は VirtualStore\Machine\Software\Wow6432Node\MyApp になります。2.2節でVirtualStoreの Wow6432Node 側も確認するのはこのためです。
仮想ストアはユーザープロファイルの一部なので、ユーザーごとに内容が異なります。「Aさんのログオンでは動くのに、Bさんでは初期値」という症状が起きる一方、64bitプロセスやサービスは同じ仮想化された見え方にはなりません。プロファイルの構造は「Windowsユーザープロファイル入門 - AppDataとNTUSER.DAT」を参照してください。2
flowchart TB
accTitle: リダイレクトと仮想化は重なり得る
accDescr: リダイレクト対象キーへの32bit x86プロセスの書き込みでは、ビューの振り分け後に条件付きで仮想化も働きます。
A["32bit x86プロセスの書き込み"] --> B["WOW64で32bitビューへ"]
B --> C["権限不足かつ仮想化の対象"]
C --> D["ユーザーのVirtualStoreへ"]
D --> E["Wow6432Node側に値が残る"]
図14: リダイレクト対象キーへの32bit x86プロセスの書き込みでは、ビューの振り分け後に条件付きで仮想化も働きます。
5.4 reg flagsでキー単位の制御を確認する
キー単位では、REG_KEY_DONT_VIRTUALIZE / REG_KEY_DONT_SILENT_FAIL / REG_KEY_RECURSE_FLAG で仮想化を制御できます。reg.exe の flags オプションで照会・設定し、公式資料にある照会例は次のとおりです。2
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 にした場合の効果は次のとおりです。2
| フラグ | 立てたときの効果 |
|---|---|
REG_KEY_DONT_VIRTUALIZE |
書き込みの仮想化を無効化。権限不足のキー作成・値設定はVirtualStoreへ転送されず、そのまま失敗します |
REG_KEY_DONT_SILENT_FAIL |
オープンの仮想化を無効化。権限不足のオープンを MAXIMUM_ALLOWED で開き直す救済をやめ、失敗させます |
REG_KEY_RECURSE_FLAG |
仮想化フラグを親キーから子へ伝播させます。効くのは変更後に作られる子キーだけで、既存の子キーには適用されません |
特定のキーで転送を止め、権限不足を表面化させたい場合は REG_KEY_DONT_VIRTUALIZE を使います。設定側は QUERY の代わりに SET を使いますが、正確なオプションの並びは reg flags /? で確認してください。
書き込みの転送、オープン時の救済、子キーへの伝播はそれぞれ別の制御です。特に REG_KEY_RECURSE_FLAG は、既存の子キーの設定を一括変更するものではありません。2
flowchart TB
accTitle: フラグの伝播は新しく作る子キーに効く
accDescr: REG_KEY_RECURSE_FLAGを有効にすると、新しく作成される子キーへ仮想化フラグを伝播し、既存の子キーは変更しません。
A["親の伝播フラグを有効にする"] --> B["変更後に作成する子キー"]
A -.-> C["既存の子キー"]
B --> D["親の仮想化フラグを伝播"]
C --> E["この変更では設定されない"]
図15: REG_KEY_RECURSE_FLAGを有効にすると、新しく作成される子キーへ仮想化フラグを伝播し、既存の子キーは変更しません。
6. インストーラーとCOM登録 ── 書く側と読む側を揃える
6.1 アプリだけを64bit化すると、設定を見失う
32bitインストーラー(32bit MSIやセットアップEXE)が HKLM\Software\会社名 へ既定の32bitビューで書いた設定は、Wow6432Node 配下に入ります。アプリだけを64bit化してインストーラーを流用すると、「インストーラーは書いた、アプリは読めない」という不一致になります。64bitインストーラーと32bitアプリの逆の組み合わせでも同様です。
対策は、インストーラーとアプリで、どのビューに書き、どのビューを読むかを揃えることです。64bit移行の過渡期には、4章の RegistryView.Registry32 で旧位置から読み取る移行処理も設計に入れます。
flowchart TB
accTitle: インストーラーとアプリが同じビューを使う
accDescr: プロセスのbitness任せにせず、機械共通の設定を書き込むビューと読み取るビューを揃えます。
A["設定を置くビューを決める"] --> B["インストーラーの書き込み先"]
A --> C["アプリの読み取り先"]
B --> D["同じビューの同じ設定"]
C --> D
E["移行中は旧ビューも確認"] -.-> C
図16: プロセスのbitness任せにせず、機械共通の設定を書き込むビューと読み取るビューを揃えます。
6.2 COM登録では、登録のビューとDLLのbitnessを確認する
HKLM\Software\Classes\CLSID はリダイレクト対象です。これは HKCR\CLSID のHKLM側に当たります。HKCR自体はHKLMとHKCUのClassesを合わせたビューなので、単独の保存先と同一視せず、その下のCLSIDの32bit/64bit分離を確認します。5
32bit側のCLSID登録と64bit側の登録は別ビューに入り、片方を登録しただけで他方の同じキーが見えるわけではありません。64bitプロセスは32bit DLLのインプロセスCOMサーバーをそのままロードできないため、この分離自体には理由があります。現場では「regsvr32で登録したのに 0x80040154(クラスが登録されていません)」として現れます。
regsvr32にも32bit版(SysWOW64側)と64bit版(System32側)があります。 どちらを使って登録したかと、クライアント・DLLのbitness、実際に参照したCLSIDのビューを確認します。COM登録の全体像は「COM/OCX/ActiveX開発でハマる登録とbitnessの罠」、レジストリ登録を不要にする選択肢は「Reg-Free COMとは」を参照してください。
flowchart TB
accTitle: インプロセスCOMの調査では2つの不一致を分ける
accDescr: CLSID登録のビューと、クライアントがDLLを読み込めるbitnessの組み合わせを別々に確認します。
A["登録したのにクラスが見つからない"] --> B["参照したCLSIDのビューを確認"]
B --> C["登録に使ったregsvr32を確認"]
C --> D["クライアントとDLLのbitnessを確認"]
D --> E["登録とロードの問題を切り分ける"]
図17: CLSID登録のビューと、クライアントがDLLを読み込めるbitnessの組み合わせを別々に確認します。
6.3 古い登録の互換機構とDLL検索は、別の話として扱う
Vista/XP時代はCLSIDなどの一部が「リダイレクト+リフレクション」で扱われていましたが、Windows 7でリフレクションは廃止されました。過去の同期を前提にせず、現在のビューを確認します。5
HKLM\SOFTWARE\Wow6432Node\Classes から HKLM\SOFTWARE\Classes\Wow6432Node への互換用シンボリックリンクもあります。これは予約パスをハードコードした既存アプリの救済用であり、新規アプリが同じ書き方をしてよいという意味ではありません。5
また、レジストリの不一致を解消しても、DLL自体のロードには別の名前解決ルールがあります。「見つからない」系の調査では「Windows DLL名前解決の仕組み - 検索順序とSxS」も確認してください。
7. 恒久対策 ── 仮想化に依存せず、保存先と移行を設計する
7.1 マニフェスト追加と64bit化の前に、HKLM書き込みを洗い出す
マニフェストの requestedExecutionLevel を指定すると、ファイル/レジストリ仮想化は無効になります。asInvoker でも同じです。Visual C++リンカーは既定でasInvokerのUACフラグメントを埋め込むため、この既定でビルドしたEXEは最初から対象外です。7
仮想化が問題になりやすいのは、マニフェストを持たないVB6、古いDelphi、古いVC++製などのレガシーEXEを動かす場面です。そこへ「とりあえずマニフェストを追加する」「ビルドし直して64bit化する」と、今まで救済されていた書き込みが本当にアクセス拒否になります。アプリのエラー処理によっては、黙って保存に失敗する形にもなります。
仮想化を無効にすることと、書き込み権限を得ることは別です。 先にHKLMへの書き込み箇所を洗い出し、保存先と失敗時の扱いを確認します。asInvoker / highestAvailable / requireAdministrator の3値と権限設計は「Windows の管理者特権が必要になるのはいつなのか」で扱っています。
flowchart TB
accTitle: 救済を外す前に保存処理を確認する
accDescr: マニフェスト追加や64bit化で仮想化が無効になるため、先に権限不足のHKLM書き込みを洗い出します。
A["レガシーEXEを移行する"] --> B["HKLM書き込みを洗い出す"]
B --> C["保存先と権限を見直す"]
C --> D["マニフェスト追加や64bit化"]
D --> E["保存と読み返しを確認"]
図18: マニフェスト追加や64bit化で仮想化が無効になるため、先に権限不足のHKLM書き込みを洗い出します。
7.2 ユーザー設定と機械共通データを分ける
Microsoftは、レジストリ仮想化を暫定的(interim)な互換技術とし、将来のWindowsから削除する意向を示しています。新規開発でこの動作に依存すべきではありません。2
設計原則は、通常のアプリが機微なシステム領域へ無理に書き込まないことです。ユーザー単位の設定はHKCUやAppDataへ、共通データは適切なACLを付けた共通の場所へ置きます。HKLMにある機械共通の設定を読む必要がある場合は、4章・6章のとおり書く側とビューを揃えます。
保存先ごとの判断は「Windowsアプリのデータ保存先の選び方」に整理しています。
flowchart TB
accTitle: 保存する範囲と場所を揃える
accDescr: ユーザー設定はユーザー単位の場所へ、共通データは適切なアクセス制御を持つ共通の場所へ置きます。
A["保存するデータの範囲"] --> B["ユーザーごとの設定"]
A --> C["機械共通のデータ"]
B --> D["HKCUやAppData"]
C --> E["適切なACLを持つ共通の場所"]
図19: ユーザー設定はユーザー単位の場所へ、共通データは適切なアクセス制御を持つ共通の場所へ置きます。
8. 実務の定石(判断表)
次の表は、症状を見てから対策を選ぶための一覧です。ユーザー設定をHKCUへ置く方針は、3.2節の通常の共有キーについての話であり、COM関連の例外まで一括して共有扱いにするものではありません。
| 状況 | やること | 理由・補足 |
|---|---|---|
| 「regeditでは見えるのにアプリから見えない」 | reg query ... /reg:64 と /reg:32 で両ビューを読み比べ |
どちらのビューに値があるかを最初に確定させる4 |
| 32bit/64bit両方の自社プロセスが同じHKLM設定を読む | 書き込み側でビューを固定し(例: 64bitビュー)、読む側全員が同じビューを明示 | RegistryView.Registry64 / KEY_WOW64_64KEY で統一63 |
コードに Wow6432Node を直書きしたくなった |
書かない。ビュー指定APIに置き換える | 物理位置はシステム予約。ARMでは WowAA32Node になる13 |
| ユーザー単位の設定の置き場所 | HKCU(またはAppData)に置く | HKCUは共有キーでbitness問題がなく、権限問題もない5 |
| レガシー32bitアプリの設定が「ユーザーによって違う」 | HKCU\Software\Classes\VirtualStore を確認 |
仮想化で転送された値がユーザーごとに溜まっている典型パターン2 |
| レガシーEXEにマニフェスト追加/64bit化する | HKLM書き込み箇所を洗い出してから実施 | 仮想化が無効になり、今まで「動いていた」書き込みが失敗し始める27 |
| COMの0x80040154 | クライアントとサーバーのbitnessを確認し、対応するregsvr32/ビューで登録を確認 | CLSID登録はビューごとに分離されている5 |
| どこを読んだか確定できない | Procmonで物理パスと結果(NAME NOT FOUNDなど)を観測 | 推測をやめて事実を見るのが最短 |
flowchart TB
accTitle: 対策を症状に対応付ける
accDescr: ビューの不一致はビュー指定で、仮想化への依存は保存先の設計で対処し、最後に実アクセスを確かめます。
A["観測した原因"] --> B["ビューが違う"]
A --> C["仮想化の救済に依存"]
B --> D["書く側と読む側を揃える"]
C --> E["保存先と権限を見直す"]
D --> F["同じ条件で再確認"]
E --> F
図20: ビューの不一致はビュー指定で、仮想化への依存は保存先の設計で対処し、最後に実アクセスを確かめます。
9. まとめ
「書いたはずの値がない」ときは、値の有無だけでなく、ビュー・ユーザー・権限を確認します。 WOW64リダイレクトは32bit/64bitの見え方を分け、UAC仮想化は条件を満たすレガシーアプリの権限不足の書き込みをVirtualStoreへ転送します。仮想化された読み取りではマージビューが返るため、本人だけが正常に見えることもあります。
リダイレクトは全キーが対象ではありません。Classesは共有、その下のCLSIDやInterfaceはリダイレクト、HKCUはほぼ共有という違いを、キーごとに確認します。Wow6432Nodeを直書きせず、reg.exe・RegistryView・Win32のフラグでビューを指定するのが実装の基本です。
インストーラーとアプリ、COM登録とクライアントは使うビューを揃え、64bit移行では旧ビューからの読み取りも設計に入れます。マニフェストを追加する前には、仮想化に頼っていたHKLM書き込みを洗い出してください。迷ったら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
-
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 ↩11 ↩12 ↩13
-
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, reg query. reg queryコマンドの/reg:32が32bitレジストリビューで、/reg:64が64bitレジストリビューでキーへアクセスするオプションであることについて。 ↩ ↩2
-
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
-
Microsoft Learn, RegistryView Enum (Microsoft.Win32). RegistryView列挙体のDefault(0)・Registry64(256)・Registry32(512)、OpenBaseKey・OpenRemoteBaseKey・FromHandleでビューを指定できること、32bit OSで64bitビューを要求した場合は32bitビューのキーが返ることについて。 ↩ ↩2 ↩3
-
Microsoft Learn, Application manifests. requestedExecutionLevel要素のasInvoker・requireAdministrator・highestAvailableの意味、requestedExecutionLevelノードを指定するとファイルおよびレジストリの仮想化が無効になること、後方互換のために仮想化を利用したい場合はこのノードを省略すること、Visual C++リンカーが既定でasInvokerのUACフラグメントをマニフェストに埋め込むことについて。 ↩ ↩2
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
Windowsアプリのデータ保存先の選び方 ── SQLite / JSON / レジストリ / Access 判断表
Windowsデスクトップアプリのデータをどこに・何で保存するか。AppData/ProgramDataの使い分け、SQLite・JSONファイル・レジストリ・Access(.accdb)それぞれの得意分野と落とし穴を判断表つきで整理し、破損対策やビット数問題まで実務目線で...
USBメモリの「安全な取り外し」は、今も必要なのか? ── 「クイック取り外し」と書き込みキャッシュから考える
USBの安全な取り外しが必要かを、クイック取り外しと高パフォーマンスの違いから判断します。書き込みキャッシュの仕組み、設定確認、取り外しの手順、「使用中」で外せない場合の対処と完了確認を説明します。
CPU使用率は低いのに音が途切れるのはなぜ? ── バッファと締め切りから考える
CPU使用率は低いのに音が途切れる理由を、バッファと補充の締め切りから説明します。対象環境、条件を一つずつ変える手順、WPR・WPAによる記録と確認、改善を原因の確定と混同しない判断基準をまとめます。
回線は速いのにRDPが重いのはなぜ? ── 入力・描画・通信を分けて調べる
回線は速いのにRDPの入力やスクロールが遅いときの切り分け手順です。入力待ち・画面処理・通信を区別し、症状別の比較、性能カウンターの測定範囲、設定を戻した確認、調査の引き継ぎを説明します。
引数はなぜ壊れるか ── Windowsのコマンドライン引数の規則
Windowsでは引数の配列は存在せず、CreateProcessに渡るのは1本の文字列で、分割は受け取り側が行います。CommandLineToArgvW・CRT・.NETの分割規則と、.NETのArgumentList・C++での正しい組み立て方を解説します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
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へ)に直すことをおすすめします。