Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win


Custom cursors over Window controls

Posted on 1998-11-24
Medium Priority
Last Modified: 2013-12-03
I want to have a user selectable cursor displayed in my
windows, including over Window controls, menu bars, and
in dialog boxes ...
When I create a dialog box I call SetClassWord, passing
GCW_HCURSOR as a parameter, while processing the WM_INITDIALOG msg.  The cursor reverts back to the arrow
when over the menu bar.

For window controls I create a window (invisible with no
width/height) for each predefined type - ie LISTBOX,
COMBOBOX, STATIC ...) and call SetClassWord. This is
done whenever a custom cursor is selected and at when
the main program terminates (to revert to the arrow cursor)

There are stil some spots when the arrow cursor shows up -
inside drop down lists and over some window controls in
common dialog boxes.  I'm wondering what other methods
can be employed to get better results.
Question by:cpirrita
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 6
LVL 22

Accepted Solution

nietod earned 400 total points
ID: 1416310
The cursor handle stored in the window class (the one you are changing) is sort of "the last option" cursor.  It is used if no other cursor is specified.  The proper approach would be to handle the WM_SETCURSOR message instead.  Details follow.
LVL 22

Expert Comment

ID: 1416311
The way that WM_SETCURSOR is used is that it is sent to the frontmost window under the mouse.  The default window procedure's handler for this message, passeses it to the parent window and this continues until there is no parent window (or until somone overides the handler rather than passing it to the default window procedure), then the procedure returns false.  The child window's handler looks at the code returned value.  It it is true, it just returns true.  If it is false, it looks to see if the mouse is actually in the window, or if it is in one of its child windows.  if it is in a child window, it returns false.  If it is in the window, it sets the cursor to the cursor specfied in the class (or to the default arrow cursor if the mouse is in the non-client area or if there is no cursor for the class).

Thus in this way, when a window gets the WM_SETCURSOR message, it gives its parent window (and its grandparent and so on) an opportunity to set the cursor themselves.  If they do so, they return TRUE (which is passed down the window chain) and the message is effectively ignored by the window.  If none of them handle the message, they will (eventually) return FALSE.  The window procedure will then change the cursor to its default.

Thus you have two ways (at least) to handle this, depending on what you need to get done.  The easiest way may be to handle this message in the parent window.  This allows you to write a WM_SETCURSOR handler for just the parent window and it will effect the behaviour in the child windows.  However, for complex cases it may be easier to handle this by writting an WM_SETCORSOR handler for each child window as well.  

Let me know if you have questions.

Author Comment

ID: 1416312
Gave it a brief try last night - I had my main window call SetCursor from the WM_SETCURSOR msg.  Some of my windows are created via DialogBox or
DialogBoxParam. I don't believe they are considered child windows; the cursor
didn't change inside those windows. That cleared up when I put a WM_SETCURSOR
handler in them.  I did experience some cursor toggling when over some pre-defined  window controls; I got around that by initially using a SetClassWord to change
the cursor to NULL for each of the pre-defined window types.

While my custom cursor now does display over the menu bars - It still reverts
back to the arrow when inside a drop-down list or on the button that displays the
dropdown list.  It also toggles when on the scroll bar control.  The controls that gives
me problems were created via the resource editor and are attributes of the control
so I'm not sure if something like a window subclass procedure needs to be
defined for those controls...

Get your Disaster Recovery as a Service basics

Disaster Recovery as a Service is one go-to solution that revolutionizes DR planning. Implementing DRaaS could be an efficient process, easily accessible to non-DR experts. Learn about monitoring, testing, executing failovers and failbacks to ensure a "healthy" DR environment.

LVL 22

Expert Comment

ID: 1416313
>> Some of my windows are created via DialogBox or DialogBoxParam.
>> I don't believe they are considered child windows
That is correct, they are not.  A child window is a window that is confined to another window's client area, like a control window or a MDI child window.  Dialogs are "owned" windows.  They can move outside of the owner's client area

>> I'm not sure if something like a window subclass procedure
>> needs to be defined for those controls
Unfortunately, it sounds like it.  If you put a handler in those control's parent window, and it made no difference, then those controls must not be passing the message to the parent window.  (What might be happening is that in some cursor locations, they handle the message directly, and in some locations they pass it on to the default window procedure where it is then passed to the parent.  This sort of logic is used in other places, like setting control colors, for example).  If that is the case, and it sounds like it is, you will have to sub-class the control and handle the message within the control.  (The easy handling would be to always call the defualt window procedure for the message, which will always call the parent window....)

One exception to this might be the scroll bars.  Do you mean "window scroll bars" that appear at the edges of the window (part of the frame) or do you mean "scroll bar controls" that is, seperate windows that appear within the client area of the window?

Author Comment

ID: 1416314
I meant scroll bar controls.  I'll try sub-classing the controls and see how that works
LVL 22

Expert Comment

ID: 1416315
Note that all the sub-classed controls can use the same window procedure (unless there are other things to be done) even though they are for different controls.

Author Comment

ID: 1416316
thanks for the info - FYI subclasing did not change the cursor for those controls.  The
breakpoints I had inthe subclass procedure hit but the cursor either stayed an arrow or
it flickered between the arrow and the custom cursor.  On the whole though things look
LVL 22

Expert Comment

ID: 1416317
>>FYI subclasing did not change the cursor for those controls
It should have.  Perhaps the default window procedure was still getting called.  Can you post the window procedure code for the sub-classed controls?

Author Comment

ID: 1416318
here's a control that displays the custom cursor while in the text box
portion of the control but flickers between ther custom cursor and the
arrow cursor when in the drop down list box.
It's defined in the resource file as :


 code for the parent window:

  switch (wMsg) {    
       hCursor   = GetCursor();    // static var
       hControl  = GetDlgItem(hDlg, IDC_AD_PLIST);  // static var
       SetClassWord(hDlg,     GCW_HCURSOR, NULL);
       SetClassWord(hControl, GCW_HCURSOR, NULL);
       lpfnMyListBox = MakeProcInstance((FARPROC)pListBox, hInstance);
       lpfnListBox   = (FARPROC)GetWindowLong(hControl, GWL_WNDPROC);
       SetWindowLong(hControl, GWL_WNDPROC, (LONG)lpfnMyListBox);
       return TRUE;
     case WM_SETCURSOR:
       return TRUE;

 subclass procedure for combobox

LONG FAR PASCAL pListBox(HWND hWnd, WORD wMsg, WORD wParam,LONG lParam)

  switch (wMsg) {
     case WM_SETCURSOR:
  return CallWindowProc(lpfnListBox, hWnd,wMsg,wParam,lParam);

Breakpoints set in the WM_SETCURSOR hit once the cursor is placed within
the drop down listbox.  I tried adding a WM_CREATE portion that set the
cursor for hWnd to NULL but the breakpoint never hit.
LVL 22

Expert Comment

ID: 1416319
In the Subclass procedure the SetCursor(GetCursor()) is a problem.  It effectively does nothing so it is hard to say what that will produce.

It should either do a SetCursor(XXXX) where XXXX is a cursor handle to the cursor you want to use.  (probably stored in a global varaible) OR it should forward the message to the parent window like
return SendMessage(GetParent(),wmsg,wParam,lParam);

In the parent (dialog) window procedure I suspect that the WM_SETCURSOR is not correct.  The hCursor  value it is using may not be initialized.  (I can't tell for sure, but if it is initialized, then this is not a problem).

Actually, the WM_INITDIALOG seems like it might try to initialize the hCursor, but if so, it does it poorly.  again it uses the GetCursor() procedure and there is no telling what that will return.  if you want to use the dialog's default cursor handle, use GetClassWord with the dialog window to get the default cursor for the dialog.

Note that for this to work hCursor must be a global or static variable.  If it is local varaible then the value set on WM_INITDIALOG won't be changed on the WM_SETCURSOR.

Author Comment

ID: 1416320
I should have explained things a little more than I did.
The main window of my program changes the cursor.  The handle to the cursor is
stored in a static variable.  it is initialized to the arrow cursor
The GetCursor function is a function of mine that returns this static variable.
By the time the code that I listed runs, I have already selected a custom cursor so
the GetCursor function returns a valid handle.  I have already verified via breakpoints
that the listed procedure and the subclass procedure are using a valid cursor handle.

It just dawned on me that from reading your comments it seems like we're talking
about different GetCursor functions - I just checked windows.h and saw another
GetCursor (oops).  I changed my function's name and re-ran it. However I
got the same results (breakpoints still show valid cursor handle)
LVL 22

Expert Comment

ID: 1416321
I can't image how it compiled with the two GetCursor()s running around.

Anyways I missed the obvious problem.  Even though your sub-class window procedure sets the cursor  (correctly it seems) it still calls the original window procedure, which ends up settign the cursor as well.  Don't call the original window procedure for this message.

The easy way to do this is to put the call to the original window procedure in a "default" section of the switch() statement.

Author Comment

ID: 1416322
I did that and verified I return 0 instead of calling CallWindowProc when the
WM_SETCURSOR msg is processed but didn't get any different results.

Featured Post

NEW Veeam Agent for Microsoft Windows

Backup and recover physical and cloud-based servers and workstations, as well as endpoint devices that belong to remote users. Avoid downtime and data loss quickly and easily for Windows-based physical or public cloud-based workloads!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

For a while now I'v been searching for a circular progress control, much like the one you get when first starting your Silverlight application. I found a couple that were written in WPF and there were a few written in Silverlight, but all appeared o…
For most people, the WrapPanel seems like a magic when they switch from WinForms to WPF. Most of us will think that the code that is used to write a control like that would be difficult. However, most of the work is done by the WPF engine, and the W…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Is your data getting by on basic protection measures? In today’s climate of debilitating malware and ransomware—like WannaCry—that may not be enough. You need to establish more than basics, like a recovery plan that protects both data and endpoints.…

636 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question