Reputation: 6866
I am using SetCursor to set the system cursor to my own image. The code looks something like this:
// member on some class
HCURSOR _cursor;
// at init time
_cursor = LoadCursorFromFile("somefilename.cur");
// in some function
SetCursor(_cursor);
When I do this the cursor does change, but on the first mouse move message it changes back to the default system arrow cursor. This is the only code in the project that is setting the cursor. What do I need to do to make the cursor stay the way I set it?
Upvotes: 8
Views: 7998
Reputation: 4335
As @Heinz Traub said the problem comes from the cursor defined on the RegisterClass
or RegisterClassEx
call. You probably have code like:
BOOL CMyWnd::RegisterWindowClass()
{
WNDCLASS wndcls;
// HINSTANCE hInst = AfxGetInstanceHandle();
HINSTANCE hInst = AfxGetResourceHandle();
if (!(::GetClassInfo(hInst, _T("MyCtrl"), &wndcls)))
{
// otherwise we need to register a new class
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
wndcls.hInstance = hInst;
wndcls.hIcon = NULL;
wndcls.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
wndcls.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = _T("MyCtrl");
if (!AfxRegisterClass(&wndcls))
{
AfxThrowResourceException();
return FALSE;
}
}
return TRUE;
}
where the wndcls.hCursor
says what cursor will be used when WM_SETCURSOR
message is thrown; it happens every time it occurs a mouse move and not only.
I solved a similar problem this way:
In the class' message map add an entry for the WM_SETCURSOR
message:
BEGIN_MESSAGE_MAP(CMyWnd, CWnd)
//... other messages
ON_WM_SETCURSOR()
END_MESSAGE_MAP()
Add the method OnSetCursor
, which will override the parent class' implementation:
BOOL CMyWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (SomeCondition())
return FALSE;
return __super::OnSetCursor(pWnd, nHitTest, message);
}
Explanation: when SomeCondition()
is true, you will not call parent's implementation. May be you want always to have a cursor not superseded with parent class behavior, so you just need an even shorter method:
BOOL CMyWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
return FALSE;
}
And the declaration of the method in the header file is:
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
Upvotes: 0
Reputation: 11
This behavior is intended to be this way. I think the most simple solution is: When creating your window class (RegisterClass || RegisterClassEx
), set the WNDCLASS.hCursor || WNDCLASSEX.hCursor
member to NULL
.
Upvotes: 1
Reputation: 5083
You need to make your HCURSOR handle not go out of scope. When the mouse moves, windows messages start flying all over the place, and it will wipe out your handle (in the example above).
Make an HCURSOR a private member of the class, and use that handle when you call LoadCursor...() and SetCursor(). When you are done, do not forget to free it, and clean it up, or you will end up with a resource leak.
Upvotes: 2
Reputation: 6866
It seems that I have two options. The first is the one that Mark Ransom suggested here, which is to respond to the windows WM_SETCURSOR
message and call SetCursor at that time based on where the mouse is. Normally windows will only send you WM_SETCURSOR
when the cursor is over your window, so you would only set the cursor in your window.
The other option is to set the default cursor for the window handle at the same time as I call SetCursor
. This changes the cursor set by the default handler to WM_SETCURSOR
. That code would look something like this:
// defined somewhere
HWND windowHandle;
HCURSOR cursor;
SetCursor(cursor);
SetClassLong(windowHandle, GCL_HCURSOR, (DWORD)cursor);
If you use the second method you have to call both SetCursor
and SetClassLong
or your cursor will not update until the next mouse move.
Upvotes: 10