ポインターを使用するための規則
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 ビット コードのネイティブ ポインターにポインターが割り当てられると、コンパイラはポインターに署名拡張し、ポインターをゼロ拡張しません。
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* に型キャストすると、コンパイラ エラーは回避されますが、関数は 64 ビット ポインター値を ul の&メモリに書き込みます。 このコードは 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_HWNDPARENT
- 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 の値と混在しないでください。 これらの型の使用方法を確認し、誤って値を切り捨てないことを確認します。