64 ビット Windows プログラミング ガイド for Delphi 64 ビット コンパイラ
Delphi で 64 ビットプログラミングを行う際の Tips です。カテゴリ分けは元ネタの "64 ビット Windows プログラミング ガイド (MSDN)" を踏襲しています。
MSDN の各トピックに関連する "Delphi の情報を補足" という形で記述してありますので、まずは元ネタのトピックをお読みください。
"64 ビット版 Windows" と言っても 2 種類あります。
Win32 アプリケーションは x86 (i386) バイナリであれば、どちらも "WOW64 (Windows 32-bit On Windows 64-bit)" という機構の上で動作しますが、x64 バイナリは IA-64 で動作せず、その逆もまた動作しません。Delphi の 32bit コンパイラが吐くバイナリは x86 で、64bit コンパイラが吐くバイナリは x64 となります。つまり、"Delphi の 64bit コンパイラが吐いたバイナリは IA-64 用 OS 上では動作しない" という事です。
アプリケーション | 16bit Windows | 32bit Windows | 64bit Windows | |
x86 | x86 | x64 | IA64 | |
Win64 (IA64) アプリケーション (Delphi コンパイラでは生成不可) |
× | × | × | ○ |
Win64 (x64) アプリケーション (Delphi 64bit コンパイラで生成可能) |
× | × | ○ | × |
Win32 (x86) アプリケーション (Delphi 32bit コンパイラで生成可能) |
× | ○ |
△ (WOW64) |
△ (WOW64) |
Win16 (x86) アプリケーション (Delphi 16bit コンパイラで生成可能) |
○ |
△ (WOW32) |
× | × |
ここでは Delphi の 64bit コンパイラが吐いたバイナリが動作する "Windows x64 (Win64)" に絞って話を進めます。
Win32 では "integer = 32bit"、"long = 32bit"、"ポインタ = 32bit" でした。Win64 では "integer = 32bit"、"long = 32bit"、"ポインタ = 64bit" となっています。これを LLP64 (IL32P64) と呼びます。OS によって採用されている抽象データ モデルは異なり、例えば Unix 系の OS では "integer = 32bit"、"long = 64bit"、"ポインタ = 64bit" な LP64 を採用しています。
抽象データ モデル | short | int | long | ポインタ |
LLP64 (IL32P64) - Windows x64 | 16bit | 32bit | 32bit | 64bit |
LP64 (I32LP64) - Unix 系 | 16bit | 32bit | 64bit | 64bit |
これを Delphi のコンパイラに当てはめてみると以下のようになります。
コンパイラ | SmallInt | Integer | LongInt | Pointer |
Delphi 64bit コンパイラ | 16bit | 32bit | 32bit | 64bit |
Delphi 32bit コンパイラ | 16bit | 32bit | 32bit | 32bit |
Delphi 16bit コンパイラ | (なし) | 16bit | 32bit | 16bit |
また、上記とは別にプラットフォーム (OS) 依存でサイズが変化する整数型 (NativeInt / NativeUint) も用意されています。
OS | NativeInt | NativeUInt |
Windows x64 (Windows 64bit) | 符号あり 64bit 整数 | 符号なし 64bit 整数 |
Windows x86 (Windows 32bit) | 符号あり 32bit 整数 | 符号なし 32bit 整数 |
Delphi がマルチプラットフォームに対応した際には、この "抽象データ モデル" が厄介な事になりそうですが、とりあえずは LLP64 だけ覚えておけばいいでしょう。
Delphi の新しいデータ型については DocWiki の "64 ビット データ型と 32 ビット データ型の比較" を参照して下さい。Delphi に於いては C++ 程にポインタに神経質になる必要はありません。この点は Delphi の利点だと思います。ただ、何らかの移植を伴う際には C++ の型を意識しなくてはならない場合があります。そのような場合には "Delphi 型と C++ 型のマッピング (DocWiki)" を参考にして下さい。
非常に悩ましいトコロですが、開発環境をコンパクトにまとめたいのなら実機の 64bit OS 上に Delphi をインストールした方がよさそうです。
仮想 PC を構築するなら、ゲスト OS を 32bit にすべきです。64bit のゲスト OS を構築するには制限があります。
Virtual PC | VMware Player | VirtualBox | ||||
32bit ホスト OS | 64bit ホスト OS | 32bit ホスト OS | 64bit ホスト OS | 32bit ホスト OS | 64bit ホスト OS | |
32bit ゲスト OS | ○ | ○ | ○ | ○ | ○ | ○ |
64bit ゲスト OS | × | × | △ (AMD-V / Intel-VT が必須) | △ (AMD-V / Intel-VT が必須) | △ (AMD-V / Intel-VT が必須) | △ (AMD-V / Intel-VT が必須) |
ゲスト OS で扱えるメモリ量はホスト OS が扱えるメモリ量に依存しますので、32bit ホスト上の 64bit ホストというのはテスト環境としての使い勝手がよくないでしょう。加えて、ゲスト OS に 64bit Windows を利用できる仮想 PC であっても、"必ず VT が必要" になります。AMD の CPU はほぼ間違いなく VT に対応していますが、Intel の CPU は個別に VT の有無を調べる必要があります。
また、Intel の Atom プロセッサ、AMD の Turion Neo / Athlon Neo / Fusion C-50 等はメモリ上限が 4GB 以下なため 64bit CPU であっても、64bit OS の利点をあまり活かすことができません。
開発用とデバッグターゲット用の OS が異なる場合にはリモートデバッガを使まなくてはならない場合があります。デバッグの組み合わせのパターンを環境別に図解してみます。
Win32 アプリを作成 | Win64 アプリを作成 |
従来通りです。 |
デバッグを行うためには Win64 アプリを実行させるための別の実機が必要となります。デバッグはリモートデバッガを介して行われます。 |
Win32 アプリを作成 | Win64 アプリを作成 |
従来通りです。但し WOW64 上での動作となるため、32bit Windows 上の動作には一部制約があります。例えば、Windows 7 には 32bit 版 Explorer が存在しないため、シェルエクステンション等のテストは行えません。 |
従来通りです。 |
Win64 アプリを作成 | |
VT が必須となります。仮想 64bit のゲスト OS は 32bit ホスト OS の影響を受けるため、利用できるメモリが極端に少なくなります。 |
"仮想 1" とほぼ同じですが、ホスト<->ゲスト間のファイルの転送を伴わないので幾分こちらの方が楽だと思います。Delphi は仮想 OS にインストールします。 |
Win32 アプリを作成 | |
デバッグはリモートデバッガを介して行われます。あまり意味はないように思えるかもしれませんが、仮想 PC とは言え "WOW64 上での実行でない" 事が重要です。 |
従来の方法を仮想 PC 上に構築しただけとなります。Delphi は仮想 OS にインストールします。 |
64bit OS 上に Delphi をインストールするのが最も無難なようです。なお、リモートデバッガを介してデバッグする場合、"プロジェクトオプション" の "リモートデバッグシンボルを含める" にチェックが入っていないとブレークポイントが効かなくなりますのでご注意ください。
Delphi では条件シンボルとして以下のような定義がなされています。
条件シンボル | 説明 |
CPUX86 |
アプリケーションターゲット CPU は x86 (IA-32) である。 X86ASM という条件シンボルも同時に定義されます。 |
CPUX64 |
アプリケーションターゲット CPU は x64 (AMD64 / Intel 64) である。 X64ASM / PUREPASCAL という条件シンボルも同時に定義されます。 |
条件シンボル | 説明 |
MSWINDOWS | アプリケーションターゲット OS は Microsoft Windows である。 |
LINUX | アプリケーションターゲット OS は Linux である。 |
POSIX | アプリケーションターゲット OS は Unix 系である。 |
MACOS | アプリケーションターゲット OS は Apple MacOS である。 |
条件シンボル | 説明 |
WIN32 | アプリケーションターゲット Windows は Windows x86 (32bit Windows) である。 |
WIN64 | アプリケーションターゲット Windows は Windows x64 (64bit Windows) である。 |
物理的に 64bit CPU が搭載されていても、CPUX64 が定義される訳ではありません。これはコンパイラがターゲットとする CPU の条件シンボルだからです。その他の条件シンボルについては "3.異なるバージョンの Delphi でソースを共有する" を参照して下さい。
Delphi ではそんなに意識する場面はないはずです。ポインタを Integer 等でキャストしている場合の修正方法は DocWiki の "ポインタ操作 (32 ビット Delphi アプリケーションの 64 ビット Windows への変換)" を参考にして下さい。
PE ヘッダ の "IMAGE_FILE_LARGE_ADDRESS_AWARE" の話だと思います。このフラグは 32bit アプリケーションではデフォルトで OFF で、64bit アプリケーションでは ON です。
ポインタと 32bit 整数のキャストを山盛り使っていてソース改変しようにも収集がつかないような状態のアプリケーションを 64bit に移行しなければならない場合には、このフラグを OFF にする事によって、アプリケーションに割り当てられる仮想メモリを 2GB 以下に制限します。「こうすれば、キャストでポインタのアドレスが削れても大抵オッケーだよね...HaHaHa」 という、後ろ向きな対処方法です。
Delphi の 64bit コンパイラ (/ リンカ) では、このフラグをオフにする事はできませんが、Delphi の 32bit コンパイラ (/ リンカ) では、逆にこのフラグをオンにする事ができます。詳細は後のトピック "WOW64 環境でのパフォーマンスとメモリ消費量" で解説します。
ワードアライメント等の話だと思いますが、Delphi ではそんなに意識する場面はないはずです。
64bit Windows では一般のアプリケーションが利用できる "サンク" 機構がないので、64bit DLL を Win32 アプリケーションから利用したり、32bit DLL を Win64 アプリケーションから利用する事はできません。
このトピックでは 32bit / 64bit プロセス間のリモート プロシージャ コール (RPC) が可能な事を応用して、DLL を相互運用する方法が説明されています。DLL を アウトプロセス COM サーバー でラッピングしたものを作れば、擬似的に DLL を相互運用できるという事のようです。
こんな感じのラッパー (サンプルは UNLHA32.DLL のラッパー) を作れば、
|
こんな感じで 32bit DLL の機能を 64bit アプリケーションから利用できるという事になります。
|
▶ ボタンを押すと動画が再生されます。
...が、ShellExecute() は 32bit アプリを起動できるのですから、ごく簡単な呼び出しの DLL であれば単純に EXE でラッピングして引数をコマンドラインパラメータで渡せばいいような気もします。
このトピックは省略します。
このセクションは省略します。
このトピックは省略します。
このトピックは省略します。
このトピックは省略します。
このトピックは省略します。
このトピックは省略します。
Win32 アプリケーションが WOW64 で動作しているのかどうかを判定するには以下のユニットを利用します。
|
WOW64 上で動作していれば IsWOW64() は True を返します。
|
基本的にアプリケーション側からはどうにもならない話が多いのですが、知識として知っておくことは無駄ではないと思います。
IMAGE_FILE_LARGE_ADDRESS_AWARE の所に "画像" という記述がありますが、画像とは全く関係なく、またもや "PE ヘッダ" の話です。
|
このように *.dpr に記述すると WOW64 上で動作する 32bit アプリケーションに 4 GB の仮想アドレス領域が割り当てられる という事です。指定しなかった場合の仮想アドレス領域は 2GB です。なお、{$SetPEFlags} コンパイラ指令は少なくとも Delphi 7 またはそれ以降で利用可能です。但し、定数が Windows.pas に定義されていない場合があります (64bit Windows 登場以前の環境ですから)。
PE ヘッダのフラグに関する Delphi のトピックとしては DocWiki の "PE (portable executable) header flags (Delphi) (DocWiki)" に詳細があります。
Delphi で環境変数を参照する事はあまりないかもしれませんが、
|
GetEnvironmentVariable() で取得する事ができます。
"64 ビット プロセス -> 32 ビット プロセス (WOW64)" または "32 ビット プロセス (WOW64) -> 64 ビット プロセス" のような WOW64 を介した処理が行われる場合にはその生成されたプロセスに対して異なる環境変数が設定されるという事になります。
OS の仕様なので、Delphi 製アプリケーションにも関連します。
OS の仕様なので、Delphi 製アプリケーションにも関連します。
Delphi では意識する必要はないと思います。
CPU のコアの数を取得したり設定する際の注意事項です。普段はあまり意識する必要はないと思いますが、例えば BDE を利用するアプリケーションの場合、プロセッサの数を制限しないと問題が発生する事があります。詳細は "53.今更ながら BDE (Borland Database Engine)" のトラブルシューティングの項目を参照して下さい。
32bit アプリケーションと 64bit アプリケーション間で情報をやりとりするための方法論です。OS の仕様なので、Delphi 製アプリケーションにも関連します。
Delphi 付属の InstallAware は MSI ベースのインストーラなので、InstallAware を使う限りあまり意識する必要はないでしょう。InstallAware の使い方については "52.InstallAware" を参考にして下さい。
基本的に、デバッガが Win64 アプリケーションの場合の話ですので、Delphi のデバッガ (Win32 アプリケーション) には当てはまりません。64bit ネイティブデバッガ (Win64 アプリケーション) で WOW64 上の Win32 アプリケーションをデバッグする手法 ("環境" の説明の逆) が記載されています。
Delphi に関する資料は DocWiki にあります。
ここに書かれていない 64bit アプリケーションでの制限事項としては、Delphi にもほぼそのまま当てはまります。大量のメモリ (4GB 以上) を積んでみてバグが出ないかテストする事は必要だと思います。
ULONG_PTR は Delphi の場合、NativeUInt のエイリアスとなっています。
Extended 型絡みのメッセージに "H1066 Extended 型の浮動小数値の精度が失われます。Double 型に丸めました" というものがあります。Delphi の場合、64bit 絡みのメッセージは "ポインタのキャスト" によるものか、"インラインアセンブラ" によるものが大半を占めると思われます。
条件コンパイルに関しては "3.異なるバージョンの Delphi でソースを共有する" を参照して下さい。
BACK |