【MFC】64bit化でレジストリの設定が読めない:Wow6432Node と KEY_WOW64_32KEY

【MFC】64bit化でレジストリの設定が読めない:Wow6432Node と KEY_WOW64_32KEY

64bit化して読めなくなる設定は、ほぼHKEY_LOCAL_MACHINE\SOFTWARE配下だけです(HKEY_CURRENT_USERにもCLSIDのような例外はあります)。CWinApp::SetRegistryKeyが使うHKEY_CURRENT_USER\Software\会社名\アプリ名 は共有キーで、32bit版と64bit版が同じ値を見ています。RegOpenKeyExが2を返したときは、キーが消えたのではなく、64bitビューという別の場所を見ています。


目次

読めなくなるキーと、そのままのキー

たいていの場合、HKEY_LOCAL_MACHINE\SOFTWARE配下のキーだけが読めなくなり、ほかのキーは64bit版でもそのまま読めます。設定が全部読めないなら、レジストリのリダイレクトとは別の原因を調べます。どのキーがリダイレクトされ、どのキーが共有されるかは、MicrosoftのRegistry Keys Affected by WOW64が表で分けています。

キーWindows 7 / Server 2008 R2以降
HKEY_LOCAL_MACHINE共有
HKEY_LOCAL_MACHINE\SOFTWAREリダイレクト
HKEY_LOCAL_MACHINE\SOFTWARE\Classes共有
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSIDリダイレクト
HKEY_CURRENT_USER共有
HKEY_CURRENT_USER\SOFTWARE共有
HKEY_CURRENT_USER\SOFTWARE\Classes共有
HKEY_CURRENT_USER\SOFTWARE\Classes\CLSIDリダイレクト

読み方には2つの決まりがあります。表にあるキーのサブキーは、別途書かれていない限り親の扱いを引き継ぎます。そして表に親が載っていないキーは共有です。HKEY_CURRENT_USER\SOFTWARE\Classes\CLSIDのように、共有の親の下にリダイレクトされる子がある形もあるので、「HKCUなら安全」とまでは言えません。

MFCアプリで実務に効くのはここです。CWinApp::SetRegistryKeyを呼んだあとのGetProfileStringやWriteProfileIntは、HKEY_CURRENT_USER\Software\<会社名>\<アプリ名>\<セクション名> を読み書きします。ここは共有キーなので、32bit版で保存したウィンドウ位置や最近使ったファイルは、64bit版に入れ替えてもそのまま読めます。プロファイル系が全滅したなら、原因はレジストリリダイレクトではありません。

一方、インストーラーが書いたライセンス情報やサーバー接続先をHKEY_LOCAL_MACHINE\SOFTWARE\会社名\製品名 に置いている場合は、そこが分岐します。同じ32/64bitの食い違いでも、ODBCのDSNは別の場所に別の実体があるので、ODBCドライバーの32bit/64bit問題を先に見てください。


RegOpenKeyExが返す2の出どころ

32bitのままだったアプリをx64でビルドし直すと書いた覚えのあるキーが開けなくなります。RegOpenKeyExWは失敗の理由を表す番号を1つ、戻り値として返します。

HKEY key = nullptr;
LSTATUS status = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE,
        L"SOFTWARE\\Contoso\\App", 0, KEY_READ, &key);
// status == ERROR_FILE_NOT_FOUND (2)
// 戻り値がエラーコードそのもの。GetLastError() は見ない。

RegOpenKeyExWは成功でERROR_SUCCESS、失敗ならシステムエラーコードを戻り値で返します。GetLastError() を読むと、直前の別のAPIが残した値や0を拾って、切り分けが1手遠回りになります。

2が返る理由は、キーが消えたからではありません。Registry Redirectorは、リダイレクト対象のキーについて32bit用と64bit用の論理ビューを別々に用意し、それぞれを別の物理的な場所へ対応づけます。32bitのときに書いた値は32bitビュー側にあり、64bitでビルドしたexeは既定で64bitビューを見ます。同じパス文字列で、違う場所を開いているわけです。

だから、レジストリエディターで探して「ある」と分かっても矛盾しません。32bitビューの実体は、ツリーの別の場所に見えているからです。ただし、そこで見つけた場所のパスを、そのままコードへ貼らないでください。次の節がその理由です。


Wow6432Nodeを直接開かない理由

やろうと思えば、Wow6432Nodeの下をパスで直接開いて32bit側の値を読む書き方もできます。リダイレクトされたキーはWow6432Nodeの下の物理的な場所へ対応づけられていて、たとえばHKEY_LOCAL_MACHINE\SoftwareはHKEY_LOCAL_MACHINE\Software\Wow6432Nodeへリダイレクトされるからです。ここまではRegistry Redirectorに書いてあるとおりで、regeditで目に見えるのもこの場所です。

ただし同じページは、続けてこう書いています。リダイレクトされたキーの物理的な場所はシステムの予約領域と考えるべきで、この場所は変わる可能性があるため、アプリケーションが直接アクセスしてはいけない、と。Accessing an Alternate Registry Viewにも、Wow6432NodeとWowAA32Nodeは予約済みで、互換性のためであっても直接使うべきではないという注記があります。

実際に場所は増えています。Windows 10 on ARMでは、32bit ARM用のキーはWow6432NodeではなくWowAA32Nodeの下です。パス文字列にWow6432Nodeを焼き込んだコードは、この時点でもう1系統ぶん外しています。Registry Keys Affected by WOW64が挙げているWow6432Node入りのシンボリックリンクも、既存アプリの互換性のためだけに定義されていて、新しいアプリケーションは使うなと明記されています。

直したい対象が「32bit側に置かれた値を64bitのexeから読む」ことなら、パスをいじるのではなく、開き方のほうを変えます。


KEY_WOW64_32KEYで32bitビューを開く

samDesiredにビューを指定するフラグを足すと、自分のビットに関係なく目的のビューを開けます。値と意味はAccessing an Alternate Registry Viewのとおりです。

フラグ値開くビュー
KEY_WOW64_64KEY0x010064bitビュー
KEY_WOW64_32KEY0x020032bitビュー
// 64bit の exe から、32bit 側に置かれた値を読む
LSTATUS status = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE,
        L"SOFTWARE\\Contoso\\App", 0,
        KEY_READ | KEY_WOW64_32KEY, &key);

このフラグを渡せるのはRegCreateKeyEx、RegDeleteKeyEx、RegOpenKeyExの3つです。RegDeleteKey(Exなし)では別ビューを指定できません。両方のフラグを同時に指定するとERROR_INVALID_PARAMETERで失敗します。そして共有キーに対しては、どちらのフラグも効果がありません。

運用でつまずきやすいのは、フラグを最初の1回しか付けない書き方です。公式のベストプラクティスは、いったんフラグ付きでビューへ入ったら、その配下のキーに対する作成・削除・オープンでも同じフラグを明示すること、としています。付け忘れた呼び出しだけが既定のビューへ戻ります。

const REGSAM view = KEY_WOW64_32KEY;   // 一度決めたら配下でも使い回す

::RegCreateKeyExW(HKEY_CURRENT_USER, subKey, 0, nullptr,
        REG_OPTION_NON_VOLATILE, KEY_WRITE | view,
        nullptr, &key, nullptr);

::RegDeleteKeyExW(HKEY_CURRENT_USER, subKey, view, 0);

両方のビューを漏れなく列挙したいときは、片方のフラグで開いたハンドルと、もう片方のフラグで開いたハンドルで、2回に分けて列挙します。1回のパスで両方は見えません。

Windows 11、Visual Studio 2026、Debug/x64で小さなMFCダイアログを作り、実際に測りました。HKEY_LOCAL_MACHINEへの書き込みは管理者権限が要るので、非管理者で書ける2系統に置き換えています。リダイレクト対象キーがHKEY_CURRENT_USER\Software\Classes\CLSID\{テスト用の名前}、共有キーがHKEY_CURRENT_USER\Software\MFC Guide\Wow64Sample\{同じ名前} です。どちらもKEY_WOW64_32KEYを付けてRegCreateKeyExで作り、開き方だけを変えて読み直しました。

開き方リダイレクト対象キー共有キー
フラグ無し(64bitビュー)2(ERROR_FILE_NOT_FOUND)0(値が読める)
KEY_WOW64_32KEY0(値が読める)―
KEY_WOW64_64KEY―0(同じ値が読める)

同じ手順で書いたのに、片方だけが2になりました。共有キーはフラグを変えても同じ値が返り、ビューで分かれていません。冒頭に書いた「HKEY_CURRENT_USER\Softwareのプロファイルは分岐しない」の実測が、この右の列です。

リダイレクト対象キーと共有キーへKEY_WOW64_32KEYで書き込み、フラグ有無で開き直したMFC実行結果
リダイレクト対象キーはフラグ無しで2、KEY_WOW64_32KEY付きで0。共有キーはどちらでも0になった実行結果

ついでに、物理的な場所も測っています。Software\Classes\Wow6432Node\CLSID\{同じ名前} をフラグ無しで開くと0が返り、KEY_WOW64_32KEYで読んだのと同じ値が取れました。つまりWow6432Nodeを直接開く書き方は、今日のWindows 11では動いてしまいます。動くから正しいわけではなく、そこが変わり得ると公式が明言している以上、依存先にはしません。

32bit版が管理者権限なしでHKEY_LOCAL_MACHINE\SOFTWAREへ書けていたなら、その値はUACのレジストリ仮想化で、ユーザーごとのHKEY_CURRENT_USER\Software\Classes\VirtualStore\MACHINE\SOFTWAREへ保存されていた可能性があります。Registry Virtualizationによると、仮想化されるのはマニフェストにrequestedExecutionLevelの指定がない32bitの対話プロセスだけで、64bitのプロセスは対象外です。そのためx64版では書き込みが5(ERROR_ACCESS_DENIED)で失敗し、32bit版が保存した値も見えなくなります。KEY_WOW64_32KEYを付けても、開くのは32bitビューでVirtualStoreではありません。ファイルの保存先が同じ仕組みでずれる例と、マニフェストでの指定や管理者権限が要る処理を分ける方法は、UAC対応: 管理者権限とファイルアクセスの注意点で扱っています。


まとめ

  • 分岐するのはHKEY_LOCAL_MACHINE\SOFTWARE配下などのリダイレクト対象キーで、CWinApp::SetRegistryKeyが使うHKEY_CURRENT_USER\Software配下は共有キーです。
  • RegOpenKeyExの戻り値がエラーコードです。2が返っても、キーが消えたとは限りません。
  • Wow6432Nodeは予約済みの物理的な場所です。パスへ焼き込まず、KEY_WOW64_32KEYで開きます。
  • ビューのフラグは、配下のキーを触るすべての呼び出しで同じものを指定します。
目次