Joe Ludwig
Joe Ludwig

Reputation: 6866

SetCursor reverts after a mouse move

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

Answers (5)

sergiol
sergiol

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.hCursorsays 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

Heinz Traub
Heinz Traub

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

LarryF
LarryF

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

Joe Ludwig
Joe Ludwig

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

Mark Ransom
Mark Ransom

Reputation: 308256

You need to respond to the Windows message WM_SETCURSOR.

Upvotes: 5

Related Questions