32 bit アプリケーションを 64 bit 環境 (WOW64) に移植する際のシステムファイル / レジストリの扱いについて


ご注意 : 下記記述のうち、レジストリ リフレクションの機能は、Windows 7 / Windows Server 2008 R2 (およびそれ以降の Windows) では削除されることが決まりました。
http://www.microsoft.com/japan/whdc/system/platform/64bit/RegReflect.mspx 

環境 :
Windows Server 2008 R2 Beta

こんにちは。

昨日のイベント講演の際、会場にてご質問を頂いたので記載します。32 bit アプリケーションを 64 bit 環境 (WOW64) へ移す際の注意点についてです。

まず、昨日もお話しましたが、基礎知識として以下を踏まえておいてください。(ご存じの方は読み飛ばしてください)

  • 基本的に、32 bit アプリケーションは、64 bit 環境の Windows Server 2008 R2 や Windows 7 などでもそのまま動作できます。これらの OS には、Windows 32 On Windows 64 (WOW64) と呼ばれるエミュレータコンポーネントが存在しており、32 bit アプリケーションを動作させた場合には、このエミュレーター上で動作します (Thunking layer により、64 bit カーネルに対するアクセスへと変換されて動作しています)。
  • しかしながら、エミュレータ上ではなく、64 bit アプリケーションとしてちゃんと動かしたいというニーズもあるでしょう。Win32 などを使用した 32 bit のネイティブアプリケーションでは 一般に、64 bit のコンパイルをしなおすことで 64 bit アプリケーションとして動作させることができます。
  •  マネージコードの場合には、一般に、.NET Framework が 32 bit と 64 bit の差異を吸収してくれます。

しかし、上記で、「一般に」、「基本的に」と書いている部分については、下記のような例外もいくつか存在するという点もご注意ください。(これも、昨日の講演でお話した内容と同じです)

  • 32 bit (WOW64)、64 bit の混在は不可能です。例えば、64 bit で動作している実行モジュールから、サードパーティなどの 32 bit のライブラリ (dll) をリンクすることは NG です
  • .NET Framework のアセンブリと言えども、ターゲットプラットフォームを指定して構成したり、ネイティブモジュールを埋め込んで作成したり、といった特別なアセンブリの作成方法が可能です。そして、こうしてビルド(構成)されたアセンブリの場合には動作が異なりますので注意してください。
    例えば、x86 アーキテクチャをターゲットとしてビルドしたあるアセンブリを 64 bit 環境で動かすと、そのアセンブリは WOW64 上の .NET Framework で実行されることになり、このモジュール上から 64 bit アーキテクチャに限定された別の dll を使うことはできなくなります。また、.NET Framework がデフォルトで入れるアセンブリの多くはネイティブモジュールを埋め込んだ形で生成されていますので、32 bit 環境にあるこうしたものもそのままコピーして持っていかないように注意しましょう (64 bit 環境上にインストールされた .NET Framework をそのまま使用してください)。

さらに、その他の例外として、 システムフォルダやレジストリなどへアクセスしているアプリケーションとかはどうなってしまうのか ? という疑問などもあろうかと思います。例えば、WOW64 のシステムフォルダは %windir%syswow64 ですから、%windir%system32 とハードコードされたプログラムは誤った動作をしてしまうのではないか?と心配されることでしょう。
ここでは、そうしたシステムフォルダ/ファイルやレジストリなどへのアクセスに関する動作について抑えておくべきポイントを記載します。

ファイルシステムリダイレクト

WOW64 上で動くプロセスでは、system32 フォルダは、ファイルシステムリダイレクター (File System Redirector) と呼ばれる仕組みによって syswow64 にリダイレクトされます。実際に例を見てみましょう。

例えば、下記のネイティブコードを 32bit アプリケーションとしてビルドします。

int _tmain(int argc, _TCHAR* argv[])
{
  wchar_t pathname[255];
  FILE *fp;

  _tsetlocale(LC_ALL, _T("")); // 日本語使用

  // システムディレクトリの取得実験
  GetSystemDirectory(pathname, 255);
  wprintf_s(L"システムディレクトリ: %sn", pathname);

  // ファイルの書き込み実験
  wcscat_s(pathname, L"\test.log");
  if(_wfopen_s(&fp, pathname, L"a"))
    return 1;
  fputs("test", fp);
  fclose(fp);
  wprintf_s(L"書き込み終了n");

  return 0;
}

これを WOW 64 の入った 64 bit 環境で実行すると、Win 32 の GetSystemDirectory 関数は syswow64 ではなく system32 のディレクトリを返します。しかし、実際にシステムディレクトリへの入出力をおこなうと、system32 にリダイレクトされます。

よって、このプログラムの出力結果は以下になり、test.log は %windir%syswow64 に出力されます。

c:Demo>Win32TestApp.exe
システムディレクトリ : C:Windowssystem32
書き込み終了

マネージコードについても同様です。以下のようなコードを作成し、構成マネージャで x86 で構成してビルドし、このアセンブリを 64 bit (x64) 環境上で実行すると、64 bit 上の .NET Framework ではなく、WOW64 が使用されて 32 bit 版の .NET Framework が動作するため、結果は上記とまったく同じように system32 と出力されますが、実際には syswow64 フォルダにファイル (test.log) が作成されます。

static void Main(string[] args)
{
    // システムディレクトリの取得実験
    string pathname = Environment.GetFolderPath(System.Environment.SpecialFolder.System);
    Console.WriteLine(String.Format("システムディレクトリ : {0}", pathname));

    // ファイルの書き込み実験
    pathname = Path.Combine(pathname, "test.log");
    using (FileStream st = File.Create(pathname))
    {
        // 何もしない . . .
        st.Close();
    }
}

ただし、例外のサブフォルダもあるので注意してください。(下記に記載されています)

MSDN ファイルシステムリダイレクター :
http://msdn.microsoft.com/en-us/library/aa384187(VS.85).aspx

また、Program Files のフォルダの場合は少々勝手が違ってきます。
上記のマネージコードのサンプルで、System.Environment.SpecialFolder.ProgramFiles のようにフォルダ情報を取得すると、返ってくるのは、%systemdrive%Program Files (x86) フォルダになります。また、%systemdrive%Program Files フォルダに対してファイルの作成などをおこなってもリダイレクトはされず、そのままそのフォルダに IO がおこなわれます。

レジストリのリダイレクトとリフレクト

同様に、レジストリについても、特定のレジストリキーを作成/オープンする場合に、別のレジストリーキーにリダイレクト (registry redirector) がおこなわれます。

今度は、プログラムではなく、レジストリエディターを使用して動作をみてみましょう。

64 bit 用のレジストリエディタは、パスの通っている %windir%regedit.exe です。一方、32 bit (WOW 64) 用のレジストリーエディタは %windir%syswow64regedit.exe になります。一般にレジストリエディタは複数インスタンスを起動することはできませんので、これら複数のバージョンのエディタを起動するには、複数起動のオプションである %windir%syswow64regedit.exe -m といった感じで起動してみてください。(32 bit 用と 64 bit 用の両方が起動します。)

例えば、以下を実施してみてください。(あとで理由はわかりますが、くれぐれも他のサブキーにアクセスせず、下記に忠実に従って実施してみてください。)

  1. 32 bit 用のレジストリエディタで、HKEY_LOCAL_MACHINESoftware の下に、新しいキーを作成します
  2. 32 bit 用のレジストリエディタで、HKEY_LOCAL_MACHINESoftware の下に新しい値を作成します
  3. 32 bit 用のレジストリエディタで、上記 1 で作成したキーの下に新しい値を作成します

これを 64 bit 用のレジストリエディタで覗くと、HKEY_LOCAL_MACHINESoftwareWow6432Node の下に、まったく同じ構成でキーや値が作成されているのがわかります。
実は、WOW 64 上での上記のレジストリアクセスは、すべて HKEY_LOCAL_MACHINESoftwareWow6432Node の下にリダイレクトされて実行されています。

リダイレクトの対象となるのは以下のキーです。(よって、ここでは詳述しませんが、HKEY_CLASSES_ROOT なども HKEY_LOCAL_MACHINESoftware のサブキーである HKEY_LOCAL_MACHINESoftwareClasses であるため、同様にリダイレクトの対象となります。)

HKEY_LOCAL_MACHINESoftware
HKEY_USERS*SoftwareClasses
HKEY_USERS*_Classes
(上記で * は、「S-1-5-18」 などの各 ID を表してします)

レジストリについては、もう 1 つ、別の概念も存在します。
今度は 32 bit 用のレジストリエディターで、HKEY_LOCAL_MACHINESoftwareClasses の下に、何か値やキーを作成してみてください。64 bit 側に行くと、さきほどと違い HKEY_LOCAL_MACHINESoftwareWow6432NodeClasses の下には何も作成されず、HKEY_LOCAL_MACHINESoftwareClasses の下に作成されているのがわかります。

これは、COM の呼び出しなどに備えて実施されているリフレクト (registry reflector) と呼ばれる仕組みで、いわば、32 bit 環境と 64 bit 環境でレジストリーキー/値の同期を取っています。

リフレクトの対象になるのは以下のキーです。

HKEY_LOCAL_MACHINESoftwareClasses
HKEY_LOCAL_MACHINESoftwareMicrosoftCOM3
HKEY_LOCAL_MACHINESoftwareMicrosoftEventSystem
HKEY_LOCAL_MACHINESoftwareMicrosoftOle
HKEY_LOCAL_MACHINESoftwareMicrosoftRpc
HKEY_USERS*SoftwareClasses
HKEY_USERS*_Classes

レジストリのこうした動作については、MSDN の下記のドキュメントに詳述されています。 

MSDN レジストリ リダイレクターとリフレクター :
http://msdn.microsoft.com/en-us/library/aa384232(VS.85).aspx
http://msdn.microsoft.com/en-us/library/aa384235(VS.85).aspx

なお、このリダイレクトは、読み込み時も同様にリダイレクト (system32 のファイルを読みにいっても、実際には syswow64 から読み込み) されます。

 

Skip to main content