Date: | November 21, 2006 / year-entry #391 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20061121-15/?p=28943 |
Comments: | 10 |
Summary: | Commenter LittleHelper asked, "Why is the cursor associated with the class and not the window?" This question makes the implicit assumption that the cursor is associated with the class. While there is a cursor associated with each window class, it is the window that decides what cursor to use. The cursor-setting process is described in... |
Commenter LittleHelper asked, "Why is the cursor associated with the class and not the window?" This question makes the implicit assumption that the cursor is associated with the class. While there is a cursor associated with each window class, it is the window that decides what cursor to use.
The cursor-setting process is described in the documentation of the
That paragraph pretty much captures the entire cursor-setting process. all I'm writing from here on out is just restating those few sentences.
The
If you don't handle the
The other, more likely, possibility is that none of the ancestor
windows cared to set the cursor.
At each return to
Here it is in pictures.
Suppose we have three windows, A, B, and C, where A is the top-level
window, B a child, and C a grandchild, and none of them do anything
special in SendMessage(hwndC, WM_SETCURSOR, ...) C's window procedure does nothing special DefWindowProc(hwndC, WM_SETCURSOR, ...) DefWindowProc forwards to parent: SendMessage(hwndB, WM_SETCURSOR, ...) B's window procedure does nothing special DefWindowProc(hwndB, WM_SETCURSOR, ...) DefWindowProc forwards to parent: SendMessage(hwndA, WM_SETCURSOR, ...) A's window procedure does nothing special DefWindowProc(hwndA) cannot forward to parent (no parent) DefWindowProc(hwndA) sets the cursor to C's class cursor DefWindowProc(hwndA) returns FALSE A's window procedure returns FALSE SendMessage(hwndA, WM_SETCURSOR, ...) returns FALSE DefWindowProc(hwndB) sets the cursor to C's class cursor DefWindowProc(hwndB) returns FALSE B's window procedure returns FALSE SendMessage(hwndB, WM_SETCURSOR, ...) returns FALSE DefWindowProc(hwndC) sets the cursor to C's class cursor DefWindowProc(hwndC) returns FALSE C's window procedure returns FALSE SendMessage(hwndC, WM_SETCURSOR, ...) returns FALSE
Observe that the
Now, of course, any of the windows along the way could have decided,
"I'm setting the cursor!" and returned
So you see, the window really does decide what the cursor is.
Yes, there is a cursor associated with the class, but it is used
only if the window decides to use it.
If you want to associate a cursor with the window, you can do it
by handling the
LittleHelper's second question:
"Many programs call
Although there is no rule forbidding you from using
Let's watch this happen. Start with the scratch program and make these changes: void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags) { Sleep(10); // just to make the flicker more noticeable SetCursor(LoadCursor(NULL, IDC_CROSS)); } // Add to WndProc HANDLE_MSG(hwnd, WM_MOUSEMOVE, OnMouseMove);
Run the program and move the mouse over the client area.
Notice that it flickers between an arrow (the class cursor,
set during |
Comments (10)
Comments are closed. |
What if an application is busy with something and isn’t processing messages? Does Windows use a default cursor or does it remember and use the registered class cursor… or other?
All the apps on my computer are well behaved enough under WinXP that I can’t think of one to use in an attempt to answer this for myself.
“The DefWindowProc function also uses this message to set the cursor to an arrow if it is not in the client area.”
Does “the client area” mean “some application”?
The cursor is not always an arrow if it is not in an application; it’s whatever the user set in the Mouse applet in the Control Panel, on the Pointers tab. It could be a hand from the “Conductor” system scheme.
That description reminded my of one of Humphrey Lyttelton’s descriptions of the game ‘One song to the tune of another.’ (Listen to http://www.bbc.co.uk/radio/aod/networks/bbc7/aod.shtml?bbc7/clue about 8:20 minutes in for an example.)
Skizz
P.S.
I’ve noticed an odd behavior with the cursor’s appearance in processes that are in a job object with the JOB_OBJECT_UILIMIT_HANDLES limit. This limit prevents processes inside the job from accessing USER handles (like HWNDs) not belonging to process inside the job. It’s quite useful in mitigating ‘shatter’ attacks.
Many applications behave normally under this limit, except that they do not always update the cursor correctly. The cursor IS updated if they let DefWindowProc set the cursor according to the the window class’s cursor, but sometimes the cursor will get "stuck" as the I-beam (or some other) cursor after moving over a textbox, and not revert back to an arrow when it’s supposed to.
To try some psychic powers of my own, I suspect that a process under the JOB_OBJECT_UILIMIT_HANDLES limit can only set the cursor while processing WM_SETCURSOR, and not at other times: apps that normally set the cursor in response to WM_MOUSEMOVE have their requests ignored under this limit.
The rationale for this Windows behavior is that you wouldn’t want a sandboxed app just setting the cursor anytime it felt like it, so it’s only allowed to set the cursor when the window manager is sure that the cursor belongs to the app– while handling WM_SETCURSOR.
This behavior can be reproduced in XP or 2003 under the "Restricted" extended SAFER level (which creates a job with the HANDLES limit), or with ulimitnt.exe and the -handles option.
Winamp, for example, won’t update the cursor at all (except in common dialog boxes). Wordpad fails to show the I-beam in the main text area. Visual Studio 2005 doesn’t display the I-beam correctly, and often makes (leaves?) the cursor invisible.
I’m not trying to blame anyone for this: it’s just another example of how not following the rules (i.e. updating the cursor at the right time) can lead to unexepected problems.
Raymond: Well, I could have assumed that you meant "logical arrow cursor" when you wrote "arrow", but I couldn’t be sure.
After all, you were talking about changing the cursor. Sorry I wasn’t more prescient.
just tested the modified scratch program, why the cursor is crosshair after some mouse moves stop? shouldn’t WM_SETCURSOR is the last message we get?
The documentation doesn’t make it entirely clear where the WM_SETCURSOR message comes from. Which is sometimes rather important to being able to divine out the messages true life and purpose.
I suspect its generated by PeekMessage/GetMessage when they see that the’yre meant to generate a WM_MOUSEMOVE message, the reason the cursor stays as a crosshair is that the WM_SETCURSOR is generated before, not after, the WM_MOUSEMOVE message.
“DefWindowProc(hwndA) sets the cursor to C’s class cursor”
Surely it would set it to *A*’s class cursor? Then
DefWindowProc(hwndB) would set it to B’s cursor, and finally
DefWindowProc(hwndC) would set it to C’s cursor.
Presumably this doesn’t result in flicker because it’s setting some
internal flag telling it to not actually draw the cursor until the
whole chain is complete. Unlike the WM_MOUSEMOVE case.
changes”. It really does set it to C’s class cursor. It has to, because
the hit-test value refers to C’s window, not A’s (or B’s). If you
wanted to set the cursor to A’s class cursor, then you would need to
have A’s hit-test code. -Raymond]
Thanks for explaining!
I still find it strange that cursors are an attribute of the window class, and not of the window instance. Maybe it is remains of constrained resources in older windows.
PingBack from http://webionaire.com/island/?p=175