レジストリの32bit/64bitリダイレクトと仮想化の落とし穴 ── Wow6432Nodeと「書いたはずの値がない」問題

· 更新日: · · レジストリ, WOW64, Wow6432Node, UAC, 64bit移行, Windows, C#, トラブルシューティング

「インストーラーが書いたライセンスキーを、アプリが起動時に読めない。regeditで見ると確かに値はある」──装置連携ソフトの64bit移行を支援していたとき、こういう報告を受けました。調べてみると、値があったのは HKLM\Software\Wow6432Node\会社名 の下。新しく64bitでビルドし直したアプリは HKLM\Software\会社名 を読みに行き、そこには何もなかった、というオチです。

この手の「書いたはずの値がない」「regeditでは見えるのにアプリから見えない」は、64bit Windows上のレジストリがプロセスのbitnessによって別の場所に見えること(WOW64のレジストリリダイレクト)と、権限不足の書き込みが別の場所へ黙って転送されること(UACのレジストリ仮想化)の2つの仕組みを知らないと、いくら目視で確認しても謎のままです。しかもどちらも「エラーにならずに成功する」ため、問題が出るのは書いた場所と読む場所が食い違った瞬間、つまり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 は「共有」、ただしその下の CLSIDInterface は「リダイレクト」という入れ子構造です。3
  • 別ビューへのアクセスは、Win32では RegOpenKeyEx などの samDesiredKEY_WOW64_64KEY / KEY_WOW64_32KEY を指定、.NETでは RegistryKey.OpenBaseKeyRegistryView.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. 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 は共有に戻り、さらにその下の CLSIDInterface は再びリダイレクトされます。「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.OpenBaseKeyRegistryView を渡します。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 / RegDeleteKeyExsamDesiredKEY_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\ClassesHKLM\Software\Microsoft\WindowsHKLM\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 flags HKLM\Software\AppKey1 QUERY のようにreg.exeで照会・設定できます。6 トラブル調査では、タスクマネージャーの詳細タブに「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\ClassesHKLM\SOFTWARE\Classes\Wow6432Node のような互換用シンボリックリンクがいくつか定義されていますが、これはWow6432Nodeをハードコードしてしまった既存アプリの救済用であり、新規アプリが使ってよいものではありません。3

なお、レジストリの分離を乗り越えてもDLL自体のロードには別の名前解決ルールが待っています。「見つからない」系の調査では「Windows DLL名前解決の仕組み - 検索順序とSxS」もあわせてどうぞ。

7. トラブルシューティング ── Procmonで「実際にどこを読んだか」を見る

仕組みを知っていても、目の前の障害で「このプロセスは結局どの物理キーを読んだのか」を確定させるには観測が必要です。ここで最強なのがSysinternalsのProcess Monitor(Procmon)です。

手順はシンプルです。

  1. Procmonを起動し、フィルターで Process Name is <対象アプリ>.exe を追加
  2. ツールバーでレジストリ操作のみ表示に絞る(Operation begins with Reg のフィルターでも可)
  3. アプリの問題の操作を再現し、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登録が見つからない」といった不具合の調査、32bitアプリ・COMコンポーネント資産の64bit移行設計、装置連携ソフトを含むWindows業務アプリの受託開発を扱っています。

参考リンク

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

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

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

  4. Microsoft Learn, RegistryView Enum (Microsoft.Win32). RegistryView列挙体のDefault(0)・Registry64(256)・Registry32(512)、OpenBaseKey・OpenRemoteBaseKey・FromHandleでビューを指定できること、32bit OSで64bitビューを要求した場合は32bitビューのキーが返ることについて。  2 3 4

  5. Microsoft Learn, reg query. reg queryコマンドの/reg:32が32bitレジストリビューで、/reg:64が64bitレジストリビューでキーへアクセスするオプションであることについて。  2 3

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

  7. Microsoft Learn, Application manifests. requestedExecutionLevel要素のasInvoker・requireAdministrator・highestAvailableの意味、requestedExecutionLevelノードを指定するとファイルおよびレジストリの仮想化が無効になること、後方互換のために仮想化を利用したい場合はこのノードを省略すること、Visual C++リンカーが既定でasInvokerのUACフラグメントをマニフェストに埋め込むことについて。  2 3

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

この記事は次のサービスページにつながります。近い入口からご覧ください。

よくある質問

この記事のテーマについて、相談時によくある質問をまとめています。

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へ)に直すことをおすすめします。

著者プロフィール

記事の著者プロフィールページです。

小村 豪

合同会社小村ソフト 代表

Windows ソフト開発、技術相談、不具合調査を中心に、既存資産が残る案件や原因が見えにくい障害調査に強みがあります。

ブログ一覧に戻る