2005年04月24日
川俣晶の縁側ソフトウェア技術雑記total 22488 count

まだまだ出るぞ、64bit移植性関連トラブル! SetWindowLongPtr APIの第3引数にLONG_PTR型を渡すと警告される!!

Written By: 川俣 晶連絡先

 りすと亭ソースの32bit化の作業がさっぱり進行しません。

 また、64bit移植性関連の問題で無駄な時間を費やしてしまいました。しかも、今回のは致命的です。

 以下はVisual Studio.NET 2003を使用することを前提に説明します。

 注: この問題そのものは、少なくとも2001年の時点でMicrosoftが認識済みの問題の再発見に過ぎません(末尾の参考リンク参照)。このコンテンツの内容は、問題の詳細をメモっておく備忘録的な役割を持って書かれるものです。

概要 §

 RegisterClassEx APIで指定するエクストラなウィンドウ領域(クラス領域ではない)にポインター値を書き込む時、SetWindowLongPtr APIを使うとされています。このAPIは、MSDNのSetWindowLongPtr Functionによれば、第3引数がLONG_PTR型であるとされています。

 しかし、この記述は32bitシステムにおいては真っ赤な嘘であり、実際には第3引数がLONGであるSetWindowLong APIにマクロ定義で置き換えられます。

 32bitシステムにおいて、LONGとLONG_PTRは完全に互換性のある型であるために、このマクロ定義は問題を発生させません。しかし、__w64キーワードによる64bit移植性の検証機能を使用する場合には、この2つの型は互換であるとは見なせません。その結果、警告が発せられます。

 では引数をLONG型で書けば良いのかというと、そのように記述すると今度は64bit環境で型が整合しません。64bit環境では、SetWindowLongPtr APIは確かに実体が存在し、その第3引数は間違いなくLONG_PTR型です。

問題の具体的な所在 §

 WinUser.hから問題の箇所を引用します。

#ifdef _WIN64

(中略)

以下の通り、64bit環境ではAPIの実体があり、戻り値と第3引数はLONG_PTR型です。

WINUSERAPI

LONG_PTR

WINAPI

SetWindowLongPtrA(

    HWND hWnd,

    int nIndex,

    LONG_PTR dwNewLong);

WINUSERAPI

LONG_PTR

WINAPI

SetWindowLongPtrW(

    HWND hWnd,

    int nIndex,

    LONG_PTR dwNewLong);

#ifdef UNICODE

#define SetWindowLongPtr  SetWindowLongPtrW

#else

#define SetWindowLongPtr  SetWindowLongPtrA

#endif // !UNICODE

#else  /* _WIN64 */

(中略)

64bitではないとき=32bitのとき、以下の通り、マクロでSetWindowLongPtrはSetWindowLongに置き換えられます。

#define SetWindowLongPtrA   SetWindowLongA

#define SetWindowLongPtrW   SetWindowLongW

#ifdef UNICODE

#define SetWindowLongPtr  SetWindowLongPtrW

#else

#define SetWindowLongPtr  SetWindowLongPtrA

#endif // !UNICODE

#endif /* _WIN64 */

 SetWindowLong APIの宣言は以下のようになっています。

WINUSERAPI

LONG

WINAPI

SetWindowLongA(

    IN HWND hWnd,

    IN int nIndex,

    IN LONG dwNewLong);

WINUSERAPI

LONG

WINAPI

SetWindowLongW(

    IN HWND hWnd,

    IN int nIndex,

    IN LONG dwNewLong);

#ifdef UNICODE

#define SetWindowLong  SetWindowLongW

#else

#define SetWindowLong  SetWindowLongA

#endif // !UNICODE

 つまり、戻り値と第3引数はLONG型です。

そして何が起こるのか §

 __w64キーワードによる64bit移植性の検証機能を使用する場合には、LONGとLONG_PTRの混用は警告される可能性があります。LONGは32bitシステムでも64bitシステムでも32bit幅であるのに対して、LONG_PTRは32bitシステムでは32bit幅であるのに対して、64bitシステムでは64bit幅になるためです。つまり、LONG_PTRからLONGへの変換は値の一部を失う可能性があり、警告の対象になります。

 従って、以下のようなコードは、__w64キーワードによる64bit移植性の検証機能が有効である32bitシステム上でコンパイルすると警告の対象となります。

コード: SetWindowLongPtr( hWnd, 0, (LONG_PTR)lpcs->lpCreateParams );

警告内容: warning C4244: '引数' : 'LONG_PTR' から 'LONG' に変換しました。データが失われているかもしれません。

 しかし、ここで第3引数をLONG_PTR型にキャストせず、LONG型にキャストすると、64bitシステムで問題が発生します。SetWindowLongPtr APIは通常ポインターを扱うことを意図して使用されますが、64bitシステムのポインターは64bit幅になります。LONG_PTR型へのキャストは正しく64bit値の受け渡しを可能にしますが、32bit幅しかないLONG型へのキャストは情報の一部が欠落することを意味します。

 つまり、正常に動作しない可能性があります。

対処方法 §

1) #ifdef _WIN64を用いて32bitと64bitで別のコードを書く

2) #pragmaディレクティブで、SetWindowLongPtr APIに関する部分のみ、warning C4244の警告を発生させないようにする (しかし、本来警告すべき内容まで抑止する恐れがあるので、リスクもある)

3) その他

関連情報 §

Blogs / Keith Brown SetWindowLongPtr and -Wp64

Bugslayer Optimize and Trim Your Code with New Switches in Visual C++ .NET -- MSDN Magazine, August 2001

Facebook

トラックバック一覧

2006年10月27日[Windows][64bit]warning C4244対策From: PGotMoL annex

これの対処方法を考えてみた。 まだまだ出るぞ、64bit移植性関連トラブル! SetWindowLongPtr APIの第3引数にLONG_PTR型を渡すと警告される!! (1) SetWindowLongPtr専用に型を定義する #ifdef _WIN64 #define _LONG_PTR LONG_PTR #else #define _LONG_PTR LONG #endif ... _ 続きを読む

このコンテンツを書いた川俣 晶へメッセージを送る

[メッセージ送信フォームを利用する]

メッセージ送信フォームを利用することで、川俣 晶に対してメッセージを送ることができます。

この機能は、100%確実に川俣 晶へメッセージを伝達するものではなく、また、確実に川俣 晶よりの返事を得られるものではないことにご注意ください。

このコンテンツへトラックバックするためのURL

https://mag.autumn.org/tb.aspx/20050424020428
サイトの表紙【技術雑記】の表紙【技術雑記】のコンテンツ全リスト 【技術雑記】の入手全リスト 【技術雑記】のRSS1.0形式の情報このサイトの全キーワードリスト 印刷用ページ

管理者: 川俣 晶連絡先

Powered by MagSite2 Version 0.36 (Alpha-Test) Copyright (c) 2004-2021 Pie Dey.Co.,Ltd.