Set cursor for child window

Hi, I currently have a Win32 window with a big listbox as a child window in it.

I also managed to set the custom cursor for the main window with this line in WinMain:
1
2
3
WNDCLASS wc;
...
wc.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR));


However this only sets the cursor for the main window, not the child window (which occupies the main part of the window).
I already searched a bit but couldn't really find anything helpful so I hope you can help me here ;)
MSDN says it is very simple. MSDN says every child control broadcasts the WM_SETCURSOR message to the parent so the parent is given the opportunity to set the child's cursor. However, I have been unable to make it work. See my post http://social.msdn.microsoft.com/Forums/en-US/windowssdk/thread/fbaec628-0155-4d6f-9d4d-67e88c0b8ded in the MS forums. Nobody couldn't or volunteered to reproduce, so I am unsure if I'm doing something wrong or not.

See if you can make it work and let me know.
Oh well I also read something about it but couldn't get it to work either.
I actually thought you have to manually send the message with SendMessage but I guess I was wrong.

Do you know what wParam of the message represents? The handle of the main window or the handle of the child window?

Could you post cour code of the window message here so I have a better idea what you tried to do..

I also found something relevant here, maybe this helps you but I also couldn't make it work -> http://www.codeguru.com/forum/showthread.php?p=355185
wParam can be both, depending on where the mouse cursor currently stands over.

The actual code is really simple:

1
2
3
4
if (LOWORD(lParam) == HTCLIENT)
{
    SetCursor(<cursor variable or a call to load cursor to load a stock cursor>);
}


The above works perfectly on the window's client area, but it doesn't work when the mouse hovers over the child control(s) in my case. Can you make it work? See if you can and let me know.
closed account (DSLq5Di1)
I had the same result webJose, seems as though subclassing may be the only solution? how annoying..
Actually I already said that I couldnt make it work.

But after looking a second time into it I found out that if you return TRUE afterwards, the child window (in my case the listbox) also had the specified cursor (I tried it with IDC_CROSS).

At the borders of the child window there was still the default cursor though, dont know why.
(edit: He seems to behave generally strange at the borders of the windows)

However this still doesnt work for custom cursors loaded with MAKEINTRESOURCE (which is exactly what I want to do), I have no idea why.
So it would be nice if someone could explain what to do here...


edit2: I forgot that I need to pass hInstance to LoadCursor if I load a custom cursor :p
So thats fixed now and it shows the cursor properly.

But I still have two problems:
- at the border of the main window I have now the custom cursor too but I would prefer to have the default cursor there (because of the different resize cursors)
- a small detail but still notable and that I already mentioned above: at the borders of the child window the cursor is still default, its only one or two pixel high but still notable so I would like to fix that too
Last edited on
Well, since sloppy9 and I are unable to make this work, please post your working code so we can understand what we are doing wrong.

As for the borders thing, it is because of the hit test value in lParam. If you only set the cursor when the hit test value is HTCLIENT you get your desired results.
Hmm maybe we dont have the same problem or we are doing something completely different? Because the code changes are really minimal, here's all of my Callback WindowProc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;
	switch (message)
	{
		case WM_PAINT:
			hdc = BeginPaint(hWnd, &ps);
			EndPaint(hWnd, &ps);
			break;
		case WM_SIZE:
			RECT hwndRect;
			GetClientRect(hWnd, &hwndRect);
			EnumChildWindows(hWnd, EnumChildProc, (LPARAM)&hwndRect);
			break;
		case WM_SETCURSOR:
			if (LOWORD(lParam) == HTCLIENT)
			{
				SetCursor(LoadCursor(GetModuleHandle(NULL), MAKEINTRESOURCE(IDC_NEWCURSOR)));
				return TRUE;
			}
			break;
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
			break;
	}
	return 0;
}


edit: I also managed now to show the cursor properly in the desired areas moreless (the horizontal lower border of the childwindow (listbox) still makes the cursor go default again but I guess I can live with that small area (unless someone knows a solution for this ;) ))

anyway, thx for the help guys, if still cant solve your problems I'll try to help as good as possible (if we even have the same problem).
Last edited on
The solution is to ignore the hit test value in lParam ONLY if wParam is NOT the HWND of the main window.

As for my Set Cursor test, my window procedure is a bit different.

First of all, it is a dialog procedure, so its return type is UINT_PTR, not LRESULT. Second, I use a BOOL variable at the beginning like this: BOOL handled = TRUE;, then I run my switch() statement, and I only change 'handled' if I want the default dialog procedure to process the message, for example in the cases where the hit test value for WM_SETCURSOR is not HTCLIENT or in the default: part of the switch().

So in essence, the same thing except I run a dialog and you don't. I'll see if I can simplify my dialog procedure to the very basics.
closed account (DSLq5Di1)
@webJose
My main window was a dialog also, though since Zapeth gave the impression it was working.. I tried a normal window and it worked as expected. After a bit of screwing around, I managed to get it working with a dialog by setting the message result!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <windows.h>
#include <tchar.h>

enum { WM_INIT = WM_USER+1, ID_BTN };

INT_PTR CALLBACK DialogProc(HWND hDialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_INIT:
		CreateWindow(_T("button"), _T("test"), WS_CHILD | WS_VISIBLE,
			         20, 20, 80, 40, hDialog, (HMENU)ID_BTN, (HINSTANCE)lParam, NULL);
		return TRUE;

	case WM_SETCURSOR:
		if (LOWORD(lParam) == HTCLIENT && GetDlgCtrlID((HWND)wParam) == ID_BTN)
		{
			SetCursor(LoadCursor(NULL, IDC_HAND));
			SetWindowLongPtr(hDialog, DWLP_MSGRESULT, TRUE);
			return TRUE;
		}
		return FALSE;

	case WM_CLOSE:
		DestroyWindow(hDialog);
		return TRUE;

	case WM_DESTROY:
		PostQuitMessage(0);
		return TRUE;
	}
	return FALSE; 
}

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd)
{
	HWND hDialog = CreateWindow(WC_DIALOG, _T("Win32 testing"), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		                        200, 200, 500, 400, NULL, NULL, hInst, NULL);

	SetWindowLongPtr(hDialog, DWLP_DLGPROC, (LONG_PTR)DialogProc);
	SendMessage(hDialog, WM_INIT, 0, (LPARAM)hInst);
	
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0) > 0)
	{
		if (!IsDialogMessage(hDialog, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return msg.wParam;
}
AWESOME sloppy9. You nailed that one for sure. Much appreciated. I was going crazy, about to post is as a bug and all. I guess it is lack of documentation.
closed account (DSLq5Di1)
Oh and you may be interested in the relevant article by Raymond Chen:-
http://blogs.msdn.com/b/oldnewthing/archive/2003/11/07/55619.aspx

I'm sure I've read about this before, and completely forgot.. *scratches head*
The solution is to ignore the hit test value in lParam ONLY if wParam is NOT the HWND of the main window.
I finally understood what you meant :d
So now pretty much everything is working as desired, thanks again for the help.

And I dont want to sound arrogant or something like this but the link I posted here contained the solution for your problem already, just like sloppy9 posted here afterwards (only that the old function SetWindowLong was posted there).
I just couldnt get it working because I am not working with a Dialog Box (as we already found out).
I checked again the original URL. Now that I knew about it I found it. My bad for skipping it before. I guess I browsed over the page too quickly and didn't realize that particular piece.
Topic archived. No new replies allowed.