ポインターを使用するための規則

32 ビットと 64 ビットの両方の Microsoft Windows 用にコンパイルするようにコードを移植するのは簡単です。 ポインターのキャストに関するいくつかの簡単な規則に従い、コードで新しいデータ型を使用するだけで済みます。 ポインター操作の規則は次のとおりです。

  1. ポインターを intlongULONGまたは 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_PTRINT_PTRを使用します (また、必要かどうかわからない場合は、万が一の場合に備えて使用しても害はありません)。 ポインターを ULONGLONGINTUINTまたは DWORD 型にキャストしないでください。

    HANDLEvoid* として定義されているため、下位 2 ビットをテスト、設定、またはクリアするために HANDLE 値を ULONG 値に型キャストすると、64 ビット Windows ではエラーになります。

  2. ポインターを切り捨てるには、PtrToLong または PtrToUlong 関数を使用します。

    32 ビット値へのポインターを切り捨てる必要がある場合は、 PtrToLong または PtrToUlong 関数 (Basetsd.h で定義) を使用します。 これらの関数は、呼び出しの間、ポインターの切り捨て警告を無効にします。

    これらの関数は慎重に使用してください。 これらの関数のいずれかを使用してポインター変数を変換した後は、ポインターとしてもう一度使用しないでください。 これらの関数はアドレスの上位 32 ビットを切り捨てます。これは通常、ポインターによって最初に参照されたメモリにアクセスするために必要です。 慎重に考慮せずにこれらの関数を使用すると、コードが脆弱になります。

  3. 64 ビット コードとしてコンパイルできるコードでPOINTER_32値を使用する場合は注意してください。 64 ビット コードのネイティブ ポインターにポインターが割り当てられると、コンパイラはポインターに署名拡張し、ポインターをゼロ拡張しません。

  4. 32 ビット コードとしてコンパイルできるコードでPOINTER_64値を使用する場合は注意してください。 コンパイラは、ポインターを 0 から拡張するのではなく、32 ビット コードでポインターに署名拡張します。

  5. 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 コードでトリックを再生しないでください。簡単でシンプルな方が良いです。

  6. ポリモーフィック インターフェイスには注意してください。

    ポリモーフィック データの DWORD パラメーターを受け取る関数は作成しないでください。 データにポインターまたは整数値を指定できる場合は、UINT_PTR型または PVOID 型を使用します。

    たとえば、 DWORD 値として型指定された例外パラメーターの配列を受け入れる関数を作成しないでください。 配列は、 DWORD_PTR 値の配列である必要があります。 したがって、配列要素はアドレスまたは 32 ビット整数値を保持できます。 (一般的な規則は、元の型が DWORD で、ポインターの幅である必要がある場合は、 DWORD_PTR 値に変換することです。そのため、対応するポインター精度型があります)。 DWORDULONG、またはその他の 32 ビット型をポリモーフィックな方法で使用するコードがある場合 (つまり、実際にはパラメーターまたは構造体メンバーにアドレスを保持させたい場合)、現在の型の代わりに UINT_PTR を使用します。

  7. 新しいウィンドウ クラス関数を使用します。

    ポインターを含むウィンドウまたはクラスのプライベート データがある場合は、コードで次の新しい関数を使用する必要があります。

    これらの関数は 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) バイトを予約します。

  8. FIELD_OFFSETを使用して、すべてのウィンドウおよびクラス データにアクセスします。

    ハードコーディングされたオフセットを使用してウィンドウ データにアクセスするのが一般的です。 この手法は、64 ビット Windows には移植できません。 コードを移植可能にするには、 FIELD_OFFSET マクロを使用してウィンドウとクラスのデータにアクセスします。 2 番目のポインターのオフセットが 4 であると想定しないでください。

  9. LPARAMWPARAMLRESULT の種類は、プラットフォームによってサイズが変更されます。

    64 ビット コードをコンパイルする場合、通常はポインターまたは整数型を保持するため、これらの型は 64 ビットに拡張されます。 これらの値は、DWORDULONG、UINTINTint、long の値と混在しないでください。 これらの型の使用方法を確認し、誤って値を切り捨てないことを確認します。