図20●64ビットWindows上で32ビットの.NET Frameworkを利用して.NETアプリケーションが動作する<br>(a)32ビットWindows上で動かした.NET 1.1アプリケーション。(b)x64 Edition上に32ビットの.NET Framework 1.1をインストールして動かした.NET 1.1アプリケーション。IntPtrの大きさが「32ビット」と表示され,32ビット・アプリケーションであることが分かる。ランタイム・バージョンはV1.1.4322と,.NET Framework 1.1のものが表示される。各アセンブリのバージョンも,1.0.5000.0と,.NET Framework 1.1のものである。
図20●64ビットWindows上で32ビットの.NET Frameworkを利用して.NETアプリケーションが動作する<br>(a)32ビットWindows上で動かした.NET 1.1アプリケーション。(b)x64 Edition上に32ビットの.NET Framework 1.1をインストールして動かした.NET 1.1アプリケーション。IntPtrの大きさが「32ビット」と表示され,32ビット・アプリケーションであることが分かる。ランタイム・バージョンはV1.1.4322と,.NET Framework 1.1のものが表示される。各アセンブリのバージョンも,1.0.5000.0と,.NET Framework 1.1のものである。
[画像のクリックで拡大表示]
図21●.NET 1.1アプリケーションが,64ビットWindows上で64ビット・アプリケーションとして動作する&lt;br&gt;図5と同じアプリケーションを,64ビットの.NET Framework 2.0上で動かした。IntPtrの大きさが「64ビット」と表示され,64ビット・アプリケーションであることが分かる。ランタイム・バージョンはV2.0.40607,各アセンブリのバージョンは,2.0.3600.0と,.NET Framework 2.0のものが表示されている。
図21●.NET 1.1アプリケーションが,64ビットWindows上で64ビット・アプリケーションとして動作する<br>図5と同じアプリケーションを,64ビットの.NET Framework 2.0上で動かした。IntPtrの大きさが「64ビット」と表示され,64ビット・アプリケーションであることが分かる。ランタイム・バージョンはV2.0.40607,各アセンブリのバージョンは,2.0.3600.0と,.NET Framework 2.0のものが表示されている。
[画像のクリックで拡大表示]
図22●64ビットWindowsで,.NETアプリケーションを動かすシーケンス
図22●64ビットWindowsで,.NETアプリケーションを動かすシーケンス
[画像のクリックで拡大表示]

既存のソースを64ビットに移植する

 32ビット・アプリケーションのソース・コードを64ビットに移植するのは,さほど難しくはない。64ビットWindowsのAPIは,32ビットWindowsのAPIと互換性があるからだ。Windowsには非常に多くのAPIがある中,非互換のAPIは10個程度である。しかも,データ型に関しては32ビットWindowsとの互換性を重視して,P64と呼ぶ,int型とlong型は32ビット,ポインタ型だけが64ビットのモデルが採用されている。

 そのままでは64ビットの大きさを持つ整数型がないことになるが,Visual C++ 2005には,64ビット用の新しいデータ型が定義されている。64ビット符号付き整数型は,「_int64」または「long long」。64ビット符号なし整数型は「unsigned _int64」である。また,Windowsのデータ型としては,「LONGLONG」や「INT64」,「ULONGLONG」や「UINT64」などが64ビット整数型として定義されている。ちなみに,これらのデータ型は,32ビット・モードでも64ビット整数型として使える。ポインタ操作さえ注意すれば,32ビット版と64ビット版を単一ソース・コードにできる。

移行しやすい.NETアプリケーション

 .NETアプリケーションならば,Win32ネイティブ・アプリケーションよりも移植しやすい。元々.NETは,OSの違いを抽象化するよう設計されているからだ。CTS(共通型システム)として,.NETではOSに依存しない形でデータ型が定義されている。

 例えば,Int32(C#ではint型)は32ビット符号付き整数型,Int64(C#ではlong型)は64ビット符号付き整数型---など,OSによらず一意に決まる。唯一OSによってデータ型の大きさが変わるのが,IntPtr型である。これはWin32のポインタ型に相当し,その大きさは32ビットWindowsでは4バイト,64ビットWindowsでは8バイトになる。

 .NETアプリケーションの動作には,.NET Frameworkが必要である。だが,.NET Framework 1.0/1.1は,32ビット版のみで64ビット版は用意されない。2005年後半に出荷が予定されている.NET Framework 2.0で,初めて64ビットWindowsに対応する。

 ただ,既存の.NET Framework 1.0/1.1対応のアプリケーションが64ビットWindowsで動かないかというと,そうではない。64ビットWindowsでも動作可能だ。それには2つの方法がある。

 1つ目は,32ビット版の.NET Framework 1.1を64ビットWindowsにインストールする方法である。この場合は,WOW64上で.NET Frameworkと.NETアプリケーションが,32ビット・アプリケーションとして動作する(図20[拡大表示])。開発ターゲットになっているバージョンの.NET Framework上で動くので,多くの場合そのまま動作するはずだ。後で移植時の注意点として説明するPInvoke(Platform Invocation Services)やCOM(Component Object Model)オブジェクトの呼び出しも問題ない。

 2つ目は,64ビット版の.NET Framework 2.0上で動かす方法である。この場合,.NETアプリケーションは,64ビット・アプリケーションとして動作する(図21[拡大表示])。だがこの場合は,アプリケーションの開発ターゲットが.NET Framework 1.0または1.1なのに対して,実行環境が.NET Framework 2.0と,バージョンが異なる。.NETアプリケーションは,開発ターゲットとした.NET Frameworkのバージョンと1対1で対応していることが基本である。.NET Frameworkのバージョンが異なると,仕様が違う部分がある。

 既存の.NET Framework 1.0/1.1向けのアプリケーションを.NET Framework 2.0で動かさざるを得ない場合は,十分にテストした上で運用すべきだ。もしもソース・コードが残っているならば,.NET Framework 2.0向けに,Visual Studio 2005で再コンパイルすることを勧める。

PInvokeとCOMに注意

 .NETアプリケーションは,一般に,.NET Framework 2.0でそのまま動作する可能性は高いし,.NET Framework 2.0への移植も容易だ。だが,PInvokeやCOMオブジェクトを呼び出している場合は別である。

 PInvokeとは,Windowsが備えるネイティブのWin32 API呼び出しのことである。また,.NETはCOMとの相互接続性があり,.NETアプリケーションからCOMオブジェクトを呼び出せる。

 これらの機能を利用した.NETアプリケーションの場合,x64 Editionで動かすときには注意が必要だ。PInvokeについては,利用しているAPIは大抵x64 Editionにも実装されているはずだが,APIに渡す引数について注意を要する。WindowsのAPIには,引数に構造体を取るものが多い。そして,WindowsのAPIが利用する構造体の中には,メンバーにその構造体の大きさを格納するものがある。構造体には,メンバーにポインタを取るものが多い。そのため,32ビットで動かしたときと64ビットで動かしたときとで,構造体の大きさが変わってしまう。そのあたりを考慮してコーディングしないと,正しい値がAPIに渡らず,誤動作を起こすことがある。

 また,COMオブジェクト呼び出しは,DLL呼び出しと同じ制約を受ける。すなわち,32ビットの.NETアプリケーションからは64ビットCOMを呼び出せず,64ビットの.NETアプリケーションからは32ビットCOMを呼べない。そのため,.NETアプリケーションを32ビットで動かしたときは問題ないが,64ビットで動かそうとしたときに64ビットのCOMが存在しないため動かないという状況があり得る。

 以上のような,PInvokeやCOMオブジェクトの呼び出しに関する制限は,開発時にターゲットとしたOSと動作時のOSが違うことに原因がある。.NET Framework 2.0では,アプリケーションの開発時にターゲットOS(正確にはCPU)を指定できるようになる。すなわち,必ず32ビットで動かしたり,必ず64ビットで動かすといった指定が可能になる。

 .NETアプリケーションが64ビットWindowsで,32ビット・アプリケーションまたは64ビット・アプリケーションのどちらで動作するかは,次のような仕組みで決まる。

 まず,32ビット版.NET Frameworkと64ビット版.NET Frameworkのどちらか一方しかインストールされていない場合は,その.NET Frameworkに従って,32ビットで動くか,64ビットで動くかが決まる。

 複雑なのは,32ビット版.NET Frameworkと64ビット版.NET Frameworkの両方がインストールされている場合だ。このときは次のようなシーケンスに従って決まる(図22[拡大表示])。

 最初に,OSがその実行ファイルをロードするときに,実行ファイルのヘッダー部分に書かれた属性を調べる。現在のWindowsの実行ファイルには,PE(ポータブル実行可能)*フォーマットと呼ぶ形式が採用されている。この形式は,プログラム本体(マシン語コード)に加えて,様々な情報が書かれた「ヘッダー」部分を備える。このヘッダー部分には,MS-DOSのEXE形式と互換性のある「MS-DOSヘッダー」と,32ビット/64ビットWindows用の「PEヘッダー」が格納されている。

 PEヘッダーはさらにいくつかの情報領域に分けられている。その中のIMAGE_FILE_HEADERと呼ぶ領域(構造体)のMachineメンバーに,対象Windowsの種類が書かれている。IMAGE_FILE_MACHINE_I386(値は16進数の014C)が設定されていると32ビット,IMAGE_FILE_MACHINE_AMD64(値は16進数の8664)が設定されているとx64対応の64ビットである。IPF版64ビットWindowsに対応した実行ファイルには,IMAGE_FILE_MACHINE_IA64(値は16進数の0200)が設定されている。

 また,PEヘッダーにあるIMAGE_OPTIONAL_HEADER領域のMagicメンバーにも,32ビット対応か64ビット対応かに関する情報が書かれている。IMAGE_NT_OPTIONAL_HDR32_MAGIC(値は16進数で010B)で32ビット,IMAGE_NT_OPTIONAL_HDR64_MAGIC(値は16進数で020B)で64ビットである(x64とIPFの違いはない)。

 これらのヘッダーに,64ビットを意味する値が設定されていると,OSはそのアプリケーションを64ビット版とみなして実行する。

 ヘッダーに32ビットを意味する値が設定されていたときは,マネージ・プログラム用のヘッダーにある32BITREQUIREDフラグを調べる。この値が立っているとWOW64上で32ビット・アプリケーションとして実行される。立っていなければ,PEヘッダーに「32ビット版」と書かれていても,64ビットのマネージ・アプリケーションとして実行される。

 32BITREQUIREDフラグは,.NET 2.0で新設されたものである。既存の.NET 1.0/1.1対応の実行ファイルで32BITREQUIREDフラグが設定されているものはない。そのため,.NET 1.0/1.1対応プログラムは,.NET Framework 2.0だけがインストールされたx64 Edition上では,どんなものでも64ビット・アプリケーションとして動作してしまう。

 だが,.NET Framework 2.0は,互換性はあるものの,.NET Framework 1.0/1.1から変更された部分がある。既存の.NET Framework 1.0/1.1対応アプリケーションをx64 Edition上で利用するときは,.NET Framework 1.0/1.1をインストールした上で,32ビット・アプリケーションとして動かすべきだ。

 一方,.NET Framework 2.0対応プログラムは,多くの場合,そのまま64ビット・アプリケーションとして動かしても正しく動作する。ただし,COMオブジェクトを呼び出していたり,PInvokeを利用していたりする場合は,対象のプラットフォームを指定しておかないと,正常に動作しなくなることがある。

 Visual Studio 2005では,プログラムの開発時に,その対象プラットフォームを指定可能だ。標準では,対象プラットフォームが「Any CPU」になっており,32ビットと64ビットの汎用の実行ファイルが生成される。必ず32ビットで動かすには対象プラットフォームを「x86」にする。これに対して必ず64ビットで動かすには対象プラットフォームを「x64」または「Itanium」にする。

 対象プラットフォームを設定するには,Visual Studioの開発環境の[プロジェクト]-[プロパティ]メニューで開く設定画面を使う。[ビルド]画面で「x86」や「x64」を選択する。「x86」を指定すると,PEヘッダーには32ビットの値(IMAGE_FILE_MACHINE_I386など)が設定され,32BITREQUIREDフラグが立てられる。これに対して「x64」を指定すると,PEヘッダーにはx64の値(IMAGE_FILE_MACHINE_AMD64)が設定される。「Any CPU」を指定したときは,PEヘッダーには32ビットの値(IMAGE_FILE_MACHINE_I386など)が設定され,32BITREQUIREDフラグは立てられない。