ポインターを使用するための規則
32 ビットと 64 ビットの両方の Microsoft Windows用にコンパイルするようにコードを移植するのは簡単です。 ポインターのキャストに関するいくつかの簡単な規則に従って、コードで新しいデータ型を使用するだけで済みます。 ポインター操作の規則は次のとおりです。
ポインターを int、 long、 ULONG、または DWORD にキャストしないでください。
ポインターをキャストしていくつかのビットをテストする、ビットを設定またはクリアする、またはその内容を操作する必要がある場合は、 UINT_PTR または INT_PTR 型を使用します。 これらの型は、32 ビットと 64 ビットの両方のWindows (たとえば、32 ビット Windowsの場合は ULONG、64 ビット Windowsの場合は _int64) のポインターのサイズにスケーリングする整数型です。 たとえば、次のコードを移植するとします。
ImageBase = (PVOID)((ULONG)ImageBase | 1);移植プロセスの一環として、次のようにコードを変更します。
ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1);必要に応じてUINT_PTRとINT_PTRを使用します (必要かどうかわからない場合は、念の場合に使用しても害はありません)。 ポインターを ULONG、LONG、INT、UINT、または DWORD 型にキャストしないでください。
HANDLE は void*として定義されているため、下位 2 ビットをテスト、設定、またはクリアするために HANDLE 値を ULONG 値に型キャストすると、64 ビット Windowsでエラーになります。
ポインターを切り捨てるには、 PtrToLong または PtrToUlong 関数を使用します。
32 ビット値へのポインターを切り捨てる必要がある場合は、 PtrToLong または PtrToUlong 関数 (Basetsd.h で定義) を使用します。 これらの関数は、呼び出し中にポインター切り捨て警告を無効にします。
これらの関数は慎重に使用してください。 これらの関数のいずれかを使用してポインター変数を変換した後は、もう一度ポインターとして使用しないでください。 これらの関数は、アドレスの上位 32 ビットを切り捨てます。これは通常、ポインターによって最初に参照されたメモリにアクセスするために必要です。 これらの関数を慎重に考慮せずに使用すると、コードが脆弱になります。
64 ビット コードとしてコンパイルされる可能性があるコードでPOINTER_32値を使用する場合は注意してください。 64 ビット コードでネイティブ ポインターに割り当てられると、コンパイラはポインターを符号拡張し、ポインターを 0 に拡張しません。
32 ビット コードとしてコンパイルされる可能性があるコードでPOINTER_64値を使用する場合は注意してください。 コンパイラは、ポインターを 0 から拡張するのではなく、32 ビット コードでポインターを符号拡張します。
OUT パラメーターを使用する場合は注意してください。
たとえば、次のように定義された関数があるとします。
void func( OUT PULONG *PointerToUlong );この関数は次のように呼び出さないでください。
ULONG ul; PULONG lp; func((PULONG *)&ul); lp = (PULONG)ul;代わりに、次の呼び出しを使用します。
PULONG lp; func(&lp);ul から &PULONG* への型キャストはコンパイラ エラーを防ぎますが、この関数は ul のメモリ&に 64 ビットのポインター値を書き込みます。 このコードは 32 ビット Windowsで動作しますが、64 ビット Windowsでデータが破損し、見つけにくい微妙な破損になります。 要するに、C コードでトリックを実行しないでください。簡単でシンプルな方が優れています。
ポリモーフィック インターフェイスには注意してください。
ポリモーフィック データの DWORD パラメーターを受け入れる関数は作成しないでください。 データにポインターまたは整数値を指定できる場合は、UINT_PTR型または PVOID 型を使用します。
たとえば、 DWORD 値として型指定された例外パラメーターの配列を受け入れる関数を作成しないでください。 配列は、 DWORD_PTR 値の配列である必要があります。 したがって、配列要素は、アドレスまたは 32 ビットの整数値を保持できます。 (一般的な規則は、元の型が DWORD で、ポインターの幅である必要がある場合は、 DWORD_PTR 値に変換することです。そのため、対応するポインター有効桁数型があります)。 DWORD、 ULONG、またはその他の 32 ビット型をポリモーフィックな方法で使用するコードがある場合 (つまり、パラメーターまたは構造体メンバーにアドレスを保持する必要がある場合)、現在の型の代わりに UINT_PTR を使用します。
新しいウィンドウ クラス関数を使用します。
ポインターを含むウィンドウまたはクラスのプライベート データがある場合は、コードで次の新しい関数を使用する必要があります。
これらの関数は、32 ビットと 64 ビットの両方のWindowsで使用できますが、64 ビット Windowsでは必要です。 次に、これらの関数を使用して移行の準備をします。
さらに、64 ビット Windowsの新しい関数を使用して、クラスのプライベート データ内のポインターまたはハンドルにアクセスする必要があります。 このようなケースを見つけるのに役立つよう、64 ビット コンパイル中に Winuser.h で次のインデックスが定義されていません。
- GWL_WNDPROC
- GWL_HINSTANCE
- GWL_HWDPARENT
- GWL_USERDATA
代わりに、Winuser.h は次の新しいインデックスを定義します。
- GWLP_WNDPROC
- GWLP_HINSTANCE
- GWLP_HWNDPARENT
- GWLP_USERDATA
- GWLP_ID
たとえば、次のコードはコンパイルされません。
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)MyWndProc);次のように変更する必要があります。
SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);WNDCLASS 構造体の cbWndExtra メンバーを設定するときは、ポインター用の十分な領域を確保してください。 たとえば、現在ポインター値の sizeof(DWORD) バイトを予約している場合は、sizeof (DWORD_PTR) バイトを予約します。
FIELD_OFFSETを使用して、すべてのウィンドウおよびクラス データにアクセスします。
ハードコーディングされたオフセットを使用してウィンドウ データにアクセスするのが一般的です。 この手法は、64 ビット Windowsには移植できません。 コードを移植しやすくするには、FIELD_OFFSET マクロを使用してウィンドウとクラス のデータ に アクセスします。 2 番目のポインターのオフセットが 4 であると想定しないでください。
LPARAM、WPARAM、LRESULT の種類は、プラットフォームによってサイズが変更されます。
64 ビット コードをコンパイルする場合、通常はポインターまたは整数型を保持するため、これらの型は 64 ビットに拡張されます。 これらの値を DWORD、ULONG、UINT、INT、int、または long の値と組み合わせないでください。 これらの型の使用方法を確認し、誤って値を切り捨てないようにします。