DefWindowProcの戻り値

解決


ひる  2011-08-30 11:29:29  No: 72882  IP: 192.*.*.*

RegisterWindowMessage()を使った独自のメッセージを親に送った際、
それが無視されてDefWindowProc()に渡ったことを判定したいのですが、
SendMessage()の戻り値が0かどうかを見てしまってよいものなのでしょうか?

つまり、上記の独自のメッセージがDefWindowProc()に渡った際には
必ず0が返るということを前提にしてしまってもよいのでしょうか?
(もちろん、親が処理した際には0以外を返すことという条件で)

DefWindowProc()の説明には、
「戻り値の意味は、メッセージによって異なります」とは書いてあるのですが、
独自のメッセージのときには0になるという解釈でよいのかが不明です。

同じような質問を2chの過去ログで見つけたのですが、結論が出ていないようなので、
こちらで詳しいかたにお伺いできればと思います。

編集 削除
仲澤@失業者  2011-08-30 13:41:46  No: 72883  IP: 192.*.*.*

まず、第一に、
一般にあるメッセージがDefWindowProc()に渡ったかどうかを調べる
必要はまったくありません。」如何なる事情でそのようなことを
調べなければいけなくなったのでしょうか(質問)。

次に「あるメッセージがDefWindowProc()に渡ったかどうかを戻り値で
判定する有効な方法はありません。」
これは、コールバックのそのメッセージのストラテジ関数のソースが
手に入らない限り絶対にわかりません。一般にその関数はDefWindowProc()
をコールするしないにかかわらず0を含む任意の値を戻すことが可能です。

最後に、RegisterWindowMessage()が戻すIDを含めて、WM_USER〜0xFFFF
までのメッセージはDefWindowProc()に渡す必要はまったくありません。
この事実によっても、そのメッセージがDefWindowProc()に渡ったか
どうかを調べるのはナンセンスであると言えます。

従って、「セッション、又はアプリケーションが登録したメッセージの
戻り値の意味を知っているのは、それを処理したコードだけである」
といえるわけで、この件についてOSは何もしません。

編集 削除
仲澤@失業者  2011-08-30 13:56:13  No: 72884  IP: 192.*.*.*

言い忘れ。orz.
登録されたメッセージを知らないウインドウが、そのメッセージ
に対して0以外の偶然の値を戻す事は、懸念しなくても良いと
考えられます。ただし、そのウインドウがDefWindowProc()を
呼んだかどうかは、わからない。ということを忘れてはいけません。

編集 削除
ひる  2011-08-30 14:11:07  No: 72885  IP: 192.*.*.*

> 一般にあるメッセージがDefWindowProc()に渡ったかどうかを調べる
> 必要はまったくありません。」如何なる事情でそのようなことを
> 調べなければいけなくなったのでしょうか(質問)。

DefWindowProc()に渡ったかというよりは、
親ウィンドウがそのメッセージを処理したかを知りたいのです。

あるタイミングで親ウィンドウにメッセージを送り、
そこでこのメッセージを捕まえてTRUEを返してくれたら○○○、
メッセージを無視したり、あるいは捕まえてもFALSEを返したら△△△、
という処理分けを行いたいと思っています。

MFCでは、親ウィンドウがメッセージマップに登録しないと、
最終的にはDefWindowProc()に渡ってしまいますよね。
このときの「親ウィンドウがメッセージを無視した」という動きを、
子ウィンドウが知りたいのです。

編集 削除
仲澤@失業者  2011-08-30 16:57:35  No: 72886  IP: 192.*.*.*

>DefWindowProc()に渡ったかというよりは、
>親ウィンドウがそのメッセージを処理したかを知りたいのです。

了解です。が、結局戻り値だけでは「処理したかどうか」もわからない
かもしれないということは今までの話で明確ですね。
ですが、0以外に処理済の意味を持たせればある程度はOKとも言えます。
こんな程度で良ければ話はここでおしまいとなります。

では、より確実にしたい場合は一般的にどうするかというと、LPARAM等に
あらかじめメンバの値を初期化した構造体を与え、SendMessage()
するのが普通です。構造体のメンバには、当然「処理した」フラグ
が必要です。このメッセージを知っているコールバックで、かつ
処理をした場合のみ、当該の構造体の「処理した」フラグをTRUEに
して戻すわけです。

編集 削除
仲澤@失業者  2011-08-30 16:58:49  No: 72887  IP: 192.*.*.*

もといorz.
LPARAMに構造体のポインタを与えるの間違いですね(vv;)。

編集 削除
ひる  2011-08-31 09:34:47  No: 72888  IP: 192.*.*.*

> 0以外に処理済の意味を持たせればある程度はOKとも言えます。

この「ある程度は」というのは、どのような意味になりますでしょうか。

DefWindowProc()は独自のメッセージのときには必ず0を返すという前提で、
SendMessage()の戻り値が0なら「親が処理しなかった」と
判断してしまってもよいということでしょうか?

DefWindowProc()が不定値を返す動作とかだったら破綻してしまうため、
このへんの仕様がどこかに書いてないかなと思ったのですが。

> より確実にしたい場合は一般的にどうするかというと、LPARAM等に
> あらかじめメンバの値を初期化した構造体を与え、SendMessage()
> するのが普通です。

たしかに、こちらの方法も考えていました。
ただ、必要な情報はBOOL値一つのみで、
それならWM_ERASEBKGNDやWM_SETCURSORなどのように、
戻り値で判断できないのかなと思って質問させていただきました。

編集 削除
YuO  2011-08-31 10:19:38  No: 72889  IP: 192.*.*.*

WM_*のように,Windowsが知っているメッセージであれば,
DefWindowProcも戻り値をその値に合わせて処理できますが,
WM_USERやWM_APPのような,戻り値の仕様をDefWindowProcが知らないメッセージに対して,
特定の値を返すことを想定はできないでしょう。
# DefWindowProc自体の仕様で書かれていない限り。

BOOLが一つなら,LPARAMにBOOL *を渡せばよいと思いますが……。

編集 削除
仲澤@失業者  2011-08-31 11:01:59  No: 72890  IP: 192.*.*.*

YuOさんもおっしゃっている通り
WM_USER〜0xFFFFのメッセージに対して
(1) DefWindowProc()は何を戻すかマニュアルには書いてない。
    未確認だが未定義だとする説がある。この場合何を戻されても
    文句が言えない。
(2) しかし、実際には0以外を戻す事実は見つかっていない。
(3) ところが、あるウインドウが当該メッセージに対して、
    必ずDefWindowProc()を呼ぶとは言えない。MFCの場合でも
    DefWindowProc()を呼ばないコードを作れるし問題なく動作する。

従って、あるウインドウに対して当該メッセージを送付した場合の
戻り値は「定義されていないので予測できない」ということになりますね。
厳密に言うと戻り値では何も判定できないが、ほとんどのウインドウは
当該メッセージに対して0を戻すようだねぇ。ということですね。

まぁ自分の場合は戻り値による判定方法を採用する勇気はありません(vv;)。

編集 削除
ロマ  2011-08-31 23:56:19  No: 72891  IP: 192.*.*.*

Adobe Reader8(ひょっとしたら別のバージョンだったかも)を
インストールすると、エクスプローラに張り付くDLLもセットされます。
このDLLがひどくって、
RegisterWindowMessageで登録したメッセージをBroadcastすると
エクスプローラは 1を返すようになります(インストール以前は0を返していた)
# オレオレ詐欺みたいなもんです。

世の中にはこんなこともあるので、送り先のクラスを見るとか、
WPARAM,LPARAMに関連した値を返すとかしたほうが良いと思います。

編集 削除
ひる  2011-09-01 09:11:34  No: 72892  IP: 192.*.*.*

みなさんありがとうございます。
おとなしくLPARAMにBOOL*を設定して(実体の初期値はFALSE)、
「処理したらこのポインタの先をTRUEに書き換えること」
というルールにしておこうかと思います。

>(1) DefWindowProc()は何を戻すかマニュアルには書いてない。
> # DefWindowProc自体の仕様で書かれていない限り。

ちなみにこれ、やはりどこにも書かれていないですか?
元々の質問がこれだったのですが(^^;

MSDN内やネット上をいろいろ探してみたのですが、
WM_APPやRegisterWindowMessage()のメッセージに対してのDefWindowProc()の
戻り値が0なのか不定(未定義)なのかを見つけることができませんでした。

もしこのあたりのドキュメントの場所をご存じのかたがいらっしゃれば、
参考までに教えていただければと思います。

編集 削除
ロマ  2011-09-01 09:46:07  No: 72893  IP: 192.*.*.*

私も見つけられませんでした。
古い話ですが、
Windows3.1のSDKに付属していたDefWindowProcの擬似コードに拠ると
未処理のメッセージにはゼロを返すようになっています。
この動作がWin32にも引き継がれているのだと、私は信じています。

編集 削除
仲澤@失業者  2011-09-01 09:58:45  No: 72894  IP: 192.*.*.*

>ちなみにこれ、やはりどこにも書かれていないですか?
自分には確認できませんでした。

元々Windows 2.xの頃は、コールバックがあるメッセージに対して
何を戻すべきなのかは「定義されていない場合は何を戻しても良い」
という雰囲気でした。これはOSがコールバックの戻り値を判定する
ケースがあまり多くなかったのが理由だと思います。
3.xになってほとんどの定義済みのメッセージに、戻すべき値が定義
されるようになりましたが、その大半が
  「このメッセージを処理したのであれば0を戻せ」
とされています。それ以外の場合は未定義です。
つまり、一般的に0を戻すとは「処理しました」という意味になるわけです。
これは  ひるさん  の定義と真っ向から衝突しますよねぇ。
この意味からも、まぁやめといたほうが良いでしょうということに
なります。

編集 削除
ひる  2011-09-06 09:26:48  No: 72895  IP: 192.*.*.*

> 私も見つけられませんでした。
> 自分には確認できませんでした。

やはりこれ書かれてないんですかね。
書かれていない=不定(未定義)ということになるのでしょうけど、
この「書かれていない」ということを断定するのは難しいものです。

> 未処理のメッセージにはゼロを返すようになっています。
> この動作がWin32にも引き継がれているのだと、私は信じています。

たしかに現在も、デバッグで追う限りは、
親ウィンドウが処理しなかった場合は必ず0が返ってきます。

これを信じてこのまま戻り値で判定しておきたいところですが、
0以外が返ってきてしまっても文句は言えないということですよね。

ご意見ありがとうございます。

編集 削除