マウスカーソルを変更するには?

解決


WAM  2004-03-02 22:14:37  No: 53239  IP: [192.*.*.*]

いつもお世話になっています。
非クライアント領域でも指定したマウスカーソルに変更することは可能でしょうか?
できればグローバルフック等を使わずに実現したいと思っているのですが…。

編集    削除
YuO  2004-03-03 01:00:13  No: 53240  IP: [192.*.*.*]

可能ですが。
http://msdn.microsoft.com/library/en-us/winui/WinUI/WindowsUserInterface/Resources/Cursors/CursorReference/CursorMessages/WM_SETCURSOR.asp

編集    削除
WAM  2004-03-03 02:33:31  No: 53241  IP: [192.*.*.*]

YuOさん、いつもありがとうございます。
上記ページにいったのですが、初心者にはいまいち理解できません。
もう少しやさしいページはありませんか?

編集    削除
Toshi  2004-03-03 03:27:13  No: 53242  IP: [192.*.*.*]

http://sysneitf.hp.infoseek.co.jp/mfc/mouse_pointer.html
この辺あたりどうでしょう。

WM_SETCURSORで検索すれば分かると思いますが、
基本的にWM_SETCURSORはクライアント領域に対して飛んでくるメッセージ
みたいです。
で、MFCでは階層的にWM_SETCURSORを処理することでクライアントと
非クライアントの形状分けを実現してるっぽいです。
(ぱっと見た目の印象なので真偽の程は分かりませんが・・・)

編集    削除
Toshi  2004-03-03 03:40:23  No: 53243  IP: [192.*.*.*]

失礼しました。適当ぶっこいてしまいました。
クライアントと非クライアントの形状分けは、DefWindowProc()で
行ってました。ですので、MFCがうんぬんは関係有りません。
(要は、DefWindowProc()では、クライアントエリアに飛んできた
  WM_SETCURSORメッセージを親ウィンドウに丸投げし、親側で制御
  してるみたいです。)

編集    削除
YuO  2004-03-03 04:00:24  No: 53244  IP: [192.*.*.*]

> WM_SETCURSORで検索すれば分かると思いますが、
> 基本的にWM_SETCURSORはクライアント領域に対して飛んでくるメッセージ
> みたいです。

どこをどう解釈するとクライアント領域に対して,という話になるのでしょうか。
MSDNをちゃんと読んでいればそのような間違いを犯すことはないと思いますが……。
ヒットテストがどのような物か,ちゃんと勉強した方がよいと思いますよ。

編集    削除
Toshi  2004-03-03 04:30:37  No: 53245  IP: [192.*.*.*]

TO: YuO

私が持っている資料には
「WM_SETCURSORメッセージは、 マウス入力がキャプチャされていないときに、 
ウィンドウ内でマウスを使用してカーソルを移動した場合に、 ウィンドウに
送られます。」と有ります。
これはクライアント領域に入ってくると解釈してますが?
(MSDNも似た様な事が書いてあると理解してます。)
具体的なイメージは湧いてませんが、基本的に親ウィンドウで制御する上でのヒットテストなんだろうなと解釈してます。(YuOさんは、とあるクライアントウィンドウプロシージャでクライアント外にあるマウスカーソルをヒットテスト出来ると仰ってます?)

編集    削除
YuO  2004-03-03 05:40:46  No: 53246  IP: [192.*.*.*]

> 「WM_SETCURSORメッセージは、 マウス入力がキャプチャされていないときに、 
> ウィンドウ内でマウスを使用してカーソルを移動した場合に、 ウィンドウに
> 送られます。」と有ります。

その通りです。
前に挙げたMSDNにも,
>The WM_SETCURSOR message is sent to a window if the mouse causes the cursor to move within a window and mouse input is not captured.
と記述があります。


> これはクライアント領域に入ってくると解釈してますが?

なぜ,そのように解釈したのですか?
ウィンドウとクライアント領域は別物です。
そうであれば,to move within a client areaのように記述されると思われますが。


> 具体的なイメージは湧いてませんが、基本的に親ウィンドウで制御する上でのヒットテストなんだろうなと解釈してます。

ヒットテストというのがわかっていないようですね……。
http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/userinput/mouseinput/aboutmouseinput.asp#_win32_The_WM_NCHITTEST_Message
なんかは読んでみるとよいでしょう。

テストを行うのはシステムです。
そして,それによってシステムは指定した場所がどの領域(クライアント・キャプション・右の枠etc.)であるかを得ます。


> (YuOさんは、とあるクライアントウィンドウプロシージャでクライアント外にあるマウスカーソルをヒットテスト出来ると仰ってます?)

「クライアントウィンドウプロシージャ」とは一体なんですか?
ウィンドウプロシージャ自体はウィンドウに関連する全てのメッセージを取り扱います。

編集    削除
Toshi  2004-03-03 22:35:05  No: 53247  IP: [192.*.*.*]

ん?ん?待てよ・・・頭が混乱してきたぞ?^^;

例えば、コントロールAとコントロールBが離れた場所にある時、AにしてみたらBはクライアントエリア外ですよね?
でもって、大抵のコントロールの場合、クライアントエリア=ウィンドウですよね?
でも、確かにメインウィンドウにしてみたら、ウィンドウ!=クライアントエリアですね、メニューやらステータスやらツールやらのエリアが有りますし・・・
でも、メインウィンドウの場合、親ウィンドウって何になるのかしら?
メインウィンドウのDefWindowProc()はどこにWM_SETCURSORを投げるのでしょうね?

編集    削除
Toshi  2004-03-03 23:28:51  No: 53248  IP: [192.*.*.*]

PS.
YuOさんに紹介されたMSDN見ました。
時間が余り無いのでざっとしか見てませんが、色々細かい制御が出来るのですね。
この辺も日本語化して欲しいなぁ・・>MSDN
所で、ヒットテストですが、ざっと見た感じ、やはりDefWindowProc()でやってるって書いてある気がするのですが・・・
いまいち、WM_SETCURSORの説明も意味不明なのですが、こいつの動きとして
該当ウィンドウにイベントが飛んでくる→該当ウィンドウのDefWindowProc()が親ウィンドウにイベントを投げる→親ウィンドウのDefWindowProc()がヒットテストを行う・・・なのかなと思ってて、DefWindowProc()に頼らないなら自分でヒットテストを行わなければならないのかなと思ってるのですが、そもそもそれ自体が私の思い違い?

余談
冒頭に「アプリはキー入力もフルサポートすべきだ」と書いてあるけど、マイクロソフト自身がやってないじゃんね?ほんとやって欲しいわ・・(特にIE)

編集    削除
YuO  2004-03-04 11:19:29  No: 53249  IP: [192.*.*.*]

> この辺も日本語化して欲しいなぁ・・>MSDN

でも,誤訳は勘弁……。
#VC++ 6.0の頃の訳は誤訳だらけで使い物にならなかった


> いまいち、WM_SETCURSORの説明も意味不明なのですが、こいつの動きとして
> 該当ウィンドウにイベントが飛んでくる→該当ウィンドウのDefWindowProc()が親ウィンドウにイベントを投げる→親ウィンドウのDefWindowProc()がヒットテストを行う・・・なのかなと思ってて、DefWindowProc()に頼らないなら自分でヒットテストを行わなければならないのかなと思ってるのですが、そもそもそれ自体が私の思い違い?

キャプチャされていないマウスの移動に対する流れは,
・マウスの移動が起きる
・一番上に表示されているウィンドウにWM_NCHITTESTが投げられる
・一番上に表示されているウィンドウにWM_SETCURSORが投げられる
・一番上に表示されているウィンドウに,WM_MOUSEMOVEまたはWM_NCMOUSEMOVEが投げられる
という風になります。

そして,WM_NCHITTESTに対するDefWindowProcの反応は,その場所に関するヒットテスト情報です。
WM_NCHITTESTに対して,例えば常にHTCAPTIONを返すようにすると,
ウィンドウのどこをドラッグしてもウィンドウが移動するようになります。
#リージョンを設定した場合などの一般的な動作。

次に,WM_SETCURSORに対するDefWindowProcの反応ですが,
1. 親ウィンドウがある場合は,親ウィンドウにWM_SETCURSORを投げる。TRUEが返ってきたら,それ以上何もせずにTRUEを返す
2. ヒットテスト情報を参考に,カーソルを設定する
  i. ヒットテストがHTCLIENTの場合
    A. カーソルがウィンドウクラスに登録されている場合,そのカーソルを設定する
    B. そうでない場合,何もしない
  ii. そうでない場合,システム既定のカーソルを設定する
3. FALSEを返す
ということになります。

既定のカーソルとしてLoadCursor(NULL, IDC_ARROW)を指定し,
ウィンドウプロシージャとしてDefWindowProcを指定したウィンドウを子ウィンドウにし,
親ウィンドウは常にSetCursor(LoadCursor(NULL, IDC_IBEAM)); return TRUE;とした場合,
子ウィンドウ上でもI-beamカーソルが表示されます。

編集    削除
WAM  2004-03-04 19:31:24  No: 53250  IP: [192.*.*.*]

みなさんありがとうございます。
すいません、自ウィンドウ外でのカーソル変更したいのですが…。

編集    削除
Toshi  2004-03-05 00:20:32  No: 53251  IP: [192.*.*.*]

To: YuOさん

>#VC++ 6.0の頃の訳は誤訳だらけで使い物にならなかった
ってゆーか、直訳っぽくて何言ってるのか良く分からない事が多いような・・

>1. 親ウィンドウがある場合は,親ウィンドウにWM_SETCURSORを投げる。TRUEが返ってきたら,それ以上何もせずにTRUEを返す
>2. ヒットテスト情報を参考に,カーソルを設定する

ですよね。
で、2.は親ウィンドウのDefWindowProc()が制御してるのですよね?

で、当初の質問に答えますと、そもそも全てのコントロールも突き詰めればウィンドウな訳で、例えばメインウィンドウのメニューにしろ、エディットボックスのスクロールバーにしろ、クライアントエリアを持ってる訳で、例えば、メインウィンドウのメニュー領域にマウスカーソルが有った時、メニュー領域が持つウィンドウプロシージャにイベントが投げられ、さらにその親ですから、メインウィンドウに投げると解釈した訳です。結果として、メインウィンドウがクライアントエリア外でもメッセージを補足出来ると・・・


で、質問者がレスってる通り、ここで言うクライアントエリア外でも補足したいと言うのは、そう言う意味でのクライアントエリアだろうなと感じたので、ウィンドウ=クライアントエリア(正確に言うとウィンドウ(若しくはコントロール)エリア内で他のコントロールエリアが無い個所)を前提に書いてました。
(ですので、私の言ったクライアントウィンドウプロシージャと言うのは、例えばメインウィンドウのメニューが持ってるウィンドウプロシージャって意味でした)

>・一番上に表示されているウィンドウにWM_NCHITTESTが投げられる

上記の解釈だと、これもすんなり理解出来たので・・・つまり、各々のクライアントエリアは自分の場所を知ってる訳で、それを親に丸投げするから親は自分の子供の範疇でクライアント/非クライアントのHotSpotを知り得るのかなと・・
(それを「親がヒットテスト(判定)」するって言葉で書いてました)

で、話は戻しますが、メインウィンドウのクライアントエリアにマウスカーソルがある時にどう言う動きになるのかがやはり掴めません。
もっとも、質問者が子ウィンドウ同士間でカーソル形状を変えたいのなら最低でも
メインウィンドウで制御出来るから問題ないと思いますが、メインウィンドウと子ウィンドウ間でもカーソル形状を変えようとしてるなら、その辺が疑問かなぁ・・と思ってまして。
(ちなみに、自アプリと他アプリ間でカーソル形状を変えたいって意味では無いですよね?>WAMさん)

編集    削除
WAM  2004-03-05 02:33:42  No: 53252  IP: [192.*.*.*]

レスありがとうございます。

>ちなみに、自アプリと他アプリ間でカーソル形状を変えたいって意味では無いですよね?

違います。
ダイアログベースで作成してます。ボタンをクリックするとカーソルが変わるようになっているのですが、自アプリのウィンドウ外に出ると、通常の矢印になってしまいます。
それを変わらないようにできたらいいなと思ってます。

編集    削除
Toshi  2004-03-05 03:12:42  No: 53253  IP: [192.*.*.*]

>ダイアログベースで作成してます。ボタンをクリックするとカーソルが変わるようになっているのですが、自アプリのウィンドウ外に出ると、通常の矢印になってしまいます。

・・それは・・自アプリと他アプリ間でってのとほぼ同意ですね・・
要は自アプリとデスクトップ間で制御したいって事ですよね?
で例えば、ちょっと離れに他アプリのダイアログが有って、そこにカーソルが移動しても同一カーソルを維持したいって事ですよね?

ん〜、既に検討済みだとは思いますが、SetSystemCursor()とかSetCursor()辺りでなんとかならないのかな?

編集    削除
WAM  2004-03-05 22:25:46  No: 53254  IP: [192.*.*.*]

Toshiさん、ありがとうございます。
SetSystemCursor()でできました!
システムのカーソルを変えることは気がつきませんでした。

編集    削除