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

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

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


目次

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

最初に切り分けるのは「どのキーが読めないのか」です。全部が読めないなら別の原因ですが、たいていは HKEY_LOCAL_MACHINE\SOFTWARE 配下だけが落ちていることがほとんどです。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 を呼んだあとの GetProfileStringWriteProfileInt は、HKEY_CURRENT_USER\Software\<会社名>\<アプリ名>\<セクション名> を読み書きします。ここは共有キーなので、32bit 版で保存したウィンドウ位置や最近使ったファイルは、64bit 版に入れ替えてもそのまま読めます。プロファイル系が全滅したなら、原因はレジストリリダイレクトではありません。

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


RegOpenKeyEx が返す 2 の出どころ

32bit のままだったアプリを x64 でビルドし直すと、書いた覚えのあるキーが開けなくなります。ここで気をつけたいのが、エラー番号の取り方です。

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 の下の物理的な場所へ対応づけられます。たとえば HKEY_LOCAL_MACHINE\SoftwareHKEY_LOCAL_MACHINE\Software\Wow6432Node へリダイレクトされます。ここまでは Registry Redirector に書いてあるとおりで、regedit で目に見えるのもこの場所です。

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

実際に場所は増えています。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);

このフラグを渡せるのは RegCreateKeyExRegDeleteKeyExRegOpenKeyEx の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 ビュー)2ERROR_FILE_NOT_FOUND0(値が読める)
KEY_WOW64_32KEY0(値が読める)
KEY_WOW64_64KEY0(同じ値が読める)

同じ手順で書いたのに、片方だけが 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では動いてしまいます。動くから正しいわけではなく、そこが変わり得ると公式が明言している以上、依存先にはしません。


まとめ

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