光标在鼠标移动后恢复原位置

8

我正在使用SetCursor将系统光标设置为自己的图像。代码大致如下:

// member on some class
HCURSOR _cursor;

// at init time
_cursor = LoadCursorFromFile("somefilename.cur");

// in some function
SetCursor(_cursor);

当我这样做时,光标确实会改变,但在第一次鼠标移动消息后,它会恢复为默认的系统箭头光标。这是项目中唯一设置光标的代码。我需要做些什么才能使光标保持我设置的方式?

5个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
10

看起来我有两个选项。第一个选项是Mark Ransom在这里建议的,即响应Windows WM_SETCURSOR消息,并在那时根据鼠标所在位置调用SetCursor。通常情况下,当光标位于您的窗口上方时,Windows才会向您发送WM_SETCURSOR,因此您只会在您的窗口中设置光标。

另一个选项是在调用SetCursor时同时设置窗口句柄的默认光标。这将改变默认处理程序设置为WM_SETCURSOR的光标。该代码看起来会像这样:

// defined somewhere
HWND windowHandle;
HCURSOR cursor;

SetCursor(cursor);
SetClassLong(windowHandle, GCL_HCURSOR, (DWORD)cursor);

如果您使用第二种方法,则必须同时调用SetCursorSetClassLong,否则光标在下次鼠标移动之前不会更新。


5
你需要响应 Windows 消息 WM_SETCURSOR。该消息的详细信息可以查看此链接

2

你需要确保 HCURSOR 句柄不超出范围。当鼠标移动时,Windows 消息会开始飞来飞去,这会抹掉你的句柄(就像上面的例子一样)。

将 HCURSOR 设置为类的私有成员,并在调用 LoadCursor...() 和 SetCursor() 时使用该句柄。完成后,请勿忘记释放和清理它,否则会导致资源泄漏。


1

这种行为是有意为之的。我认为最简单的解决方案是:在创建窗口类(RegisterClass || RegisterClassEx)时,将 WNDCLASS.hCursor || WNDCLASSEX.hCursor 成员设置为 NULL


1
将其更改为NULL并没有解决问题。与其允许光标更改,不如说光标始终是<->而不是正常的箭头。并且我可以使用Spy++ 64位验证,WM_SETCURSOR消息始终被发送。但是,我还是点了赞,因为你让我学到了新东西。 - sergiol

0
正如 @Heinz Traub 所说,问题来自于在 RegisterClassRegisterClassEx 调用中定义的光标。你可能有以下代码:
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;
}

其中wndcls.hCursor指定了在抛出WM_SETCURSOR消息时使用的光标;它会在每次鼠标移动时发生,而不仅仅是一次。

我通过以下方式解决了类似的问题:

在类的消息映射中添加一个WM_SETCURSOR消息的条目:

BEGIN_MESSAGE_MAP(CMyWnd, CWnd)
    //... other messages
    ON_WM_SETCURSOR()
END_MESSAGE_MAP()
添加方法OnSetCursor,该方法将覆盖父类的实现:
BOOL CMyWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
    if (SomeCondition())
        return FALSE;

    return __super::OnSetCursor(pWnd, nHitTest, message);
}
解释:当SomeCondition()为真时,您将不会调用父类的实现。也许您希望始终具有不被父类行为取代的光标,因此您只需要一个更短的方法:
BOOL CMyWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
    return FALSE;
}

而在头文件中该方法的声明为:

afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,