The cursor isn’t associated with a window or a window class; it’s associated with a thread group


In my earlier discussion of the fact that changing a class property affects all windows of that class, commenters LittleHelper and Norman Diamond wanted to know "Why is the cursor associated with class and not a window?"

This is another one of those questions that start off with an invalid assumption. The cursor is not associated with a class. The cursor is not associated with a window. The cursor is associated with an input state. (Initially, each thread has its own input state, but functions like Attach­Thread­Input can cause threads to share their input states.)

As we saw when we explored the process by which the cursor gets set, the cursor-setting process is initiated by the WM_SETCURSOR message, which is percolated up and down the window hierarchy until somebody calls Set­Cursor and returns TRUE to say "Okay, I set the cursor. You can stop searching now." And that cursor remains in effect until somebody else in the same thread group calls Set­Cursor.

It so happens that the Def­Window­Proc function, when asked to set a cursor, will use the window's class cursor. But that's just the default in the absence of any customization to the contrary. If you want to customize the cursor when it is over a particular window, then use the customization; don't go changing the default. If you change the default, then you affect what happens to all the other windows of the class. Just handle the WM_SETCURSOR message to establish your "per-window cursor". (And you can be even more specific than just per-window. For example, you might decide to show a hand cursor if the user is over a hyperlink but an arrow cursor otherwise.)

Many of the fields in the WND­CLASS structure are merely defaults which are applied to windows of the class. You can still override them on a per-window basis.

Field How to override
lpfn­Wnd­Proc Set­Window­Long­Ptr(GWLP_WNDPROC)
hIcon Send­Message(WM_SETICON)
hCursor Handle the WM_SETCURSOR message
hbrBackground Handle the WM_ERASEBKGND message
lpsz­Menu­Name Set­Menu()

(This is the same table I wrote up some time ago, but the original table didn't have an entry for the window procedure, so this table is slightly more complete.

Comments (21)
  1. Joshua says:

    Seriously, some people cannot read documentation. No wonder they have trouble.

  2. LR says:

    I think, this topic is a good example why I find low-level Win32 GUI programming hard to understand, complicated and therefore too time-consuming by a large factor. I never know if I have read enough about the problem at hand, so I never know if I'm finally doing the right thing. Thanks god that Delphi and .NET saves me most of the time from digging into dirt like this.

  3. Robert W. says:

    When people comment on this blog they often:

    1) Criticise Microsoft's design decisions

    2) Criticise Microsoft's developer documentation

    The ongoing theme of this blog is that the response to (1) is: the design decisions were taken in the past and if Microsoft changed them now it would break existing applications. The response to (2) can be summarised by this quote from Raymond:

    "And extra thanks to the people who took it as an opportunity to complain about the documentation. I mean, duh, if the documentation were perfect, I wouldn't have written this series in the first place. Though these people also neglected to read all of the documentation; they looked only at the function description page. There's more to documentation than dry function descriptions, people! The function description is a reference; you go there when you already know what's going on and you just need to fine-tune a detail. The real learning happens in the overviews and articles."

     blogs.msdn.com/…/1747713.aspx

    So let's look at where the "real learning" happens when it comes to cursors.

    The Class Cursor section of the Window Class overview states:

    "A window can set the cursor shape by calling the SetCursor function whenever the window receives the WM_MOUSEMOVE message."

     msdn.microsoft.com/…/ms633574(v=VS.85).aspx

    The WNDCLASSEX Structure description states:

    "hCursor: A handle to the class cursor. … If this member is NULL, an application must explicitly set the cursor shape whenever the mouse moves into the application's window."

     msdn.microsoft.com/…/ms633577(v=VS.85).aspx

    Only if we read the Cursors overview do we get the intended way of specifying that a window should have a cursor that differs from the class cursor:

    "To override the class cursor, process the WM_SETCURSOR message."

     msdn.microsoft.com/…/ms648379(v=VS.85).aspx

    Finally the Cursors examples section gives some example code showing SetCursor being called in a handler for WM_SETCURSOR.

     msdn.microsoft.com/…/ms648380(v=VS.85).aspx

    So the conclusion is not surprising: You must read around the subject before you launch into writing any code. (Or you will fall into the trap of calling SetCursor in response to WM_MOUSEMOVE and end up with a flickering mouse cursor.)

    But I want to make one further point. This blog entry was inspired by comments from LittleHelper and Norman Diamond.

    LittleHelper says: "Why is cursor associated with class and not window?"

    Norman Diamond replies: "Yeah when you find that out, please let me know too."

    And Raymond criticises them for "starting off with an invalid assumption".

    No they are simply questioning Microsoft's design. An alternative design could have allowed the developer to specify the cursor for a window when calling CreateWindow and change it later by passing a window handle and a cursor handle to SetCursor. I know this is not the way it works but these commenters are simply voicing their opinion that the design would be much more intuitive if it did work that way.

    It is not an "invalid assumption" to question a design where the (default) cursor is associated with a class instead of directly with a window.

    [When you say "Why X instead of Y?" you are assuming that X is true. In this case, the implied assumption is that the cursor is associated with a class. It is not. -Raymond]
  4. Cheong says:

    Actually, if you think of a Window Class as a attribute template for the window you're creating, it'd make some sense.

    By reading the documentation for RegisterClassEx(), I find it a bit amazing that "whether the input text is passed as Unicode" is determined by which version of RegisterClassExA/W() called, instead of some field in WNDCLASSEX or when calling CreateWindowExA/W().

  5. Pierre B. says:

    Joshua: if someone had asked me what is associated with the cursor, I'm pretty I'd have gotten it wrong too, even if I've been programming Win32 for 15 years. It's hard to know with foresight when your assumptions are wrong. It's hard enough to know when you *got* assumptions, as a start.

    I really like the varied nature of how things are overridden: function call, send a message, handle a message… :) Historical (and probably practical) reasons, I suppose.

    [Even though there are a lot of different ways of customizing something, be thankful that the defaults are all in one place! -Raymond]
  6. 640k says:

    To have the application execute logic on every mouse move to set the mouse cursor is BAD design.

    The most intuitive should of course be to associate the icon with the window, one and only one time. Everything else is BAD design.

    @Robert W: You must read around the subject before you launch into writing any code.

    This is BAD design.

    [Under your model, how would a Web browser or word processor change the cursor depending on which part of the document you are over? Does every link, table, movable object, resizing handle, etc. become a child window? (Besides, the application is already executing logic for mouse moves: OnMouseMove.) -Raymond]
  7. Gabe says:

    I think that if I were designing the system, I would make it so that the cursor is a property of a window. When the mouse is over a window, the cursor is set to the icon associated with that window. To implement something like a web browser where the cursor changes depending on what element it's over, you would call SetCursor(hWnd, hCursor) in the OnMouseMove handler. Otherwise you'd just call SetCursor once after CreateWindow and be done with it.

    That said, the current system isn't that bad. The overhead of setting the cursor on every mouse move is probably trivial compared to the work already done (hit testing, sending messages, dispatching messages, etc.).

    [That would work, except for the flicker: The mouse enters a window, the window manager sets the cursor to the [old] window cursor, then OnMouseMove changes the cursor to the hand (because you're over a link). Windows was developed on 4.77MHz machines (that's MHz), so the flicker would be quite noticeable. I guess you could wait until OnMouseMove returns to set the cursor. Of course, if you want this behavior, you can easily implement it in your WM_SETCURSOR handler. (You also lose the "parent can override child's cursor" feature which static controls rely on, but you could have a special hCursor value that means "Use my parent window's cursor.") It also adds 2 bytes to every window. Back then, we counted bytes since we had only 65536 of them. -Raymond]
  8. 640k says:

    Are we now talkning about 16-bit windows? In 16-bit windows window icons WAS in fact associated with window class, no process/thread/task boundaries was enfored at all. Class icons was os global.

    [We're talking about cursors, not icons. -Raymond]
  9. Gabe says:

    I think my system would have two special cursors. One would be the "use parent window's cursor" (probably the default for most windows) and the other would be an "owner draw" type of cursor where the window would get WM_SETCURSOR messages.

  10. AsmGuru62 says:

    So, calling SetCursor on every mouse move is overhead now?

    MFC allocates a new memory block on each mouse move (in some cases)!

  11. LR says:

    @Gabe: "That said, the current system isn't that bad. The overhead of setting the cursor on every mouse move is probably trivial compared to the work already done (hit testing, sending messages, dispatching messages, etc.)."

    The performance is not the problem, but the inherent complexity. Among other things, as far as I know, there is no straight-forward way to implement the most basic programming task: Have a single-threaded GUI application to show the hourglass cursor over all its top-level windows during the entire operation of (for example) saving a file or inserting some databass records, especially when there is a common dialog in the code path. At least for internal applications, within a larger company, you have hundreds of cases where you need just that.

    How do you explain such concepts to co-workers who only understand VBA-like programming style, to some degree?

    Raymond: "(Besides, the application is already executing logic for mouse moves: OnMouseMove.)"

    Why? Most of the time, my applications don't handle raw mouse movements. Yes, there may be some logic in the framework, but still: What logic are you talking about?

    [Per-window cursors won't solve the "I want the hourglass for all windows in my process" problem either. (Actually, per-window cursors make it worse, because you need to somehow tell all the other windows in your process "Hey, I know your cursor is an arrow, but could you make it an hourglass for me? Thanks.") Oh, and if your application code is letting the framework handle mouse moves, then you can also let the framework handle setting the cursor. All you're doing is playing games with labels. -Raymond]
  12. Robert W. says:

    Of course more recently Microsoft had an opportunity to re-design this stuff from scratch. And what did they do? They allowed the developer to set the cursor on a per-window basis.

    msdn.microsoft.com/…/system.windows.forms.control.cursor.aspx

  13. Cheong says:

    @Robert: Aren't we talking about API level of working now? If we're talking about functionalities delivered by higher level framework, I think we can set MousePointer per window/control since VB…

  14. Arno says:

    What is the recommended way to trigger Windows to resend WM_SETCURSOR, because my decision for a particular window about which cursor should be shown, changed?

  15. 640k says:

    > Per-window cursors won't solve the "I want the hourglass for all windows in my process" problem either.

    Process object and windows should NOT be this closely coupled. It's BAD design.

    Setting a cursor of a top level window could of course automatically set in for child controls which choose to inherit the cursor. Other windows properties are inherited in this way.

    The application callback should not even be called in these cases if the system was properly engineered. Things as this is why windows always have been slow and complex.

    [Okay, so say you're Notepad. You have a frame window, and you have a child edit control which says "I want a custom cursor (the I-bar cursor)." Now the frame window is about to load a file, so it wants to put up the hourglass. Oops, that doesn't put up the hourglass for the child window since the child window does not specify "cursor: inherit". -Raymond]
  16. Paul M. Parks says:

    @Arno: Judging from the documentation for WM_SETCURSOR, just move the mouse. The notification is "[s]ent to a window if the mouse causes the cursor to move within a window and mouse input is not captured."

    msdn.microsoft.com/…/ms648382(VS.85).aspx

  17. Arno says:

    @Arno: Judging from the documentation for WM_SETCURSOR, just move the mouse. The notification is "[s]ent to a window if the mouse causes > the cursor to move within a window and mouse input is not captured."

    What if I don't move the mouse? SendInput? PostMessage( WM_MOUSEMOVE )? To which window? Lots of questions…

  18. Alex Grigoriev says:

    @Arno:

    Whenever you want to change the mouse cursor, you can call SetCursor by yourself, no problems. WM_SETCURSOR just gives you an unified place to update your cursor if anything changed.

    If I remember correctly, it's also sent when your window gets uncovered, even though the mouse may not move at that time. The documentation doesn't mention that.

  19. Alex Grigoriev says:

    @Arno:

    Whenever you want to change the mouse cursor, you can call SetCursor by yourself, no problems. WM_SETCURSOR just gives you an unified place to update your cursor if anything changed.

    If I remember correctly, it's also sent when your window gets uncovered, even though the mouse may not move at that time. The documentation doesn't mention that.

  20. Drizzt says:

    I'm clearly a poor programmer.

    If someone had asked me what is associated with the cursor, I'd have replied "Who the hell cares."

  21. 640k says:

    @Drizzt

    Cursor usually changes when moved over a different window. It should obvious be associated with a window, as .net does. Instead some "smart" person at ms choose to invent an abstract class object which cursor is associated with. Not very smart it seems, because every program which wants to set window cursors now has to implement and execute logic at run time. Run time overhead, but more importantly, a code maintenance overhead. This should have been a declarative window property.

    [16-bit Windows was designed with the philosophy of letting applications decide how they want to do things. If they want a per-window declarative property, they could implement that in their WM_SETCURSOR handler. It's easy to implement declarative if you have imperative. It's hard to go the other way. -Raymond]

Comments are closed.

Skip to main content