You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++ WinAPI 鼠标移出窗口后自定义系统光标仅生效一次问题排查

代码存在的核心错误

  • 光标句柄提前销毁:SetSystemCursor 函数执行后会接管传入的光标句柄,此时你主动调用 DestroyCursor 销毁句柄,会导致第一次使用后 my_cur 和 def_arrow_cur 都变为无效句柄,第二次调用 SetSystemCursor 时传入无效句柄自然无法生效,这是效果仅触发一次的直接原因。
  • API使用场景错误:SetSystemCursor 是全局修改系统默认光标的API,会影响所有运行中的程序,完全不适合实现单个应用的光标切换需求,还会导致程序退出后系统光标无法自动恢复的问题。
  • 鼠标事件监听配置错误:你在 WM_CREATE 和 WM_MOUSELEAVE 中注册的是 TME_HOVER 悬停事件,和你需要监听的鼠标离开逻辑不匹配,多余的事件注册会带来不必要的性能开销。

正确实现方案

你的需求不需要修改全局系统光标,只需要处理 WM_SETCURSOR 消息,结合鼠标进出状态来设置光标即可:

  1. 提前加载好两个光标,程序运行期间不要销毁,退出时再统一销毁
  2. 监听WM_MOUSEMOVE时注册TME_LEAVE事件,监听鼠标离开
  3. 处理WM_SETCURSOR消息,根据bMouseInside状态返回对应的光标
  4. 程序退出时销毁所有加载的光标资源

修正后核心代码示例

// 全局光标变量,运行期间不要销毁
HCURSOR def_arrow_cur = LoadCursor(NULL, IDC_ARROW);
HCURSOR my_cur = LoadCursorFromFile(TEXT("my.cur"));

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    TRACKMOUSEEVENT tme;
    static BOOL bMouseInside = FALSE;
    switch (message) 
    {
        case WM_CREATE:
            {
                RECT rt;
                POINT pt;
                GetClientRect(hWnd, &rt);
                ClientToScreen(hWnd, (LPPOINT)&rt);
                ClientToScreen(hWnd, (LPPOINT)&rt+1);
                GetCursorPos(&pt);
                bMouseInside = PtInRect(&rt, pt);
            }
            break;
        case WM_MOUSEMOVE:
            if (!bMouseInside) {
                bMouseInside = TRUE;
                tme.cbSize = sizeof(TRACKMOUSEEVENT);
                tme.dwFlags = TME_LEAVE;
                tme.hwndTrack = hWnd;
                tme.dwHoverTime = HOVER_DEFAULT;
                _TrackMouseEvent(&tme);
            }
            break;
        case WM_MOUSELEAVE:
            bMouseInside = FALSE;
            break;
        case WM_SETCURSOR:
            // 仅客户区走自定义光标逻辑
            if (LOWORD(lParam) == HTCLIENT) {
                SetCursor(bMouseInside ? def_arrow_cur : my_cur);
                return TRUE;
            }
            return DefWindowProc(hWnd, message, wParam, lParam);
        case WM_DESTROY:
            // 退出前销毁加载的自定义光标
            DestroyCursor(my_cur);
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

如果你确实需要全局修改系统光标(不推荐),需要注意不要销毁传入SetSystemCursor的句柄,并且在程序退出时调用SystemParametersInfo(SPI_SETCURSORS, 0, NULL, 0)恢复系统所有默认光标。

内容的提问来源于stack exchange,提问作者Miriel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 10:15:08