【问题标题】:Why do I get a WM_MOUSELEAVE after enabling window?为什么我在启用窗口后得到 WM_MOUSELEAVE?
【发布时间】:2016-09-24 05:15:25
【问题描述】:

启用禁用的子窗口后,我尝试在 WM_ENABLE 中打开鼠标跟踪,仅当鼠标光标悬停在使用 TrackMouseEvent()TRACKMOUSEEVENT 结构的 dwFlags 设置为 TME_LEAVE 的窗口上时。 TrackMouseEvent() 返回 TRUE,但在调用它之后我立即收到 WM_MOUSELEAVE 消息。这仅在 2 个条件下发生。在第一个条件下,将光标移到子窗口之外,按 Enter 键禁用窗口,然后将光标移到子窗口上并按 Space 键。在第二种情况下,将光标移到窗口上,按 Enter 键禁用它,然后在按 Space 键之前将光标移动 1 个像素或更多,然后按 Space 键。如果您重新测试第二个条件,而不是在按 Space 键之前移动光标,如果您在按 Enter 键后立即按 Space 键,则鼠标跟踪将正确打开。我已经非常努力地解决了这个问题,但到目前为止我还不是很幸运。有人可以修复此代码并解释为什么在我尝试打开鼠标跟踪时会取消它吗?

#include <windows.h>


const WCHAR g_szChildClassName[] = L"Childclass////";
HINSTANCE g_hInst;


LRESULT CALLBACK ChildProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static BOOL bMouseTracking = FALSE;

    switch(msg)
    {
    case WM_PAINT:
        {
            HDC hdc;
            PAINTSTRUCT ps;
            hdc = BeginPaint(hwnd, &ps);
            if(hdc)
            {
                HBRUSH hbr = CreateSolidBrush(bMouseTracking?RGB(255, 0, 0):RGB(0, 0, 255));
                if(hbr)
                {
                    FillRect(hdc, &ps.rcPaint, hbr);
                    DeleteObject(hbr);
                }
                EndPaint(hwnd, &ps);
            }
        }
        break;

    case WM_MOUSEMOVE:
        if(!bMouseTracking) 
        {
            TRACKMOUSEEVENT tme = { 0 };
            tme.cbSize    = sizeof(TRACKMOUSEEVENT);
            tme.dwFlags   = TME_LEAVE;
            tme.hwndTrack = hwnd;
            bMouseTracking = TrackMouseEvent(&tme);
            InvalidateRect(hwnd, 0, TRUE);
        }
        break;

    case WM_MOUSELEAVE:
        bMouseTracking = FALSE;
        InvalidateRect(hwnd, 0, TRUE);
        break;

    case WM_ENABLE:
        if(wParam) 
        {
            RECT rc;
            if(GetWindowRect(hwnd, &rc))
            {
                POINT pt;
                if(GetCursorPos(&pt))
                    if(PtInRect(&rc, pt)) 
                    {
                        TRACKMOUSEEVENT tme = { 0 };
                        tme.cbSize    = sizeof(TRACKMOUSEEVENT);
                        tme.dwFlags   = TME_LEAVE;
                        tme.hwndTrack = hwnd;
                        //TrackMouseEvent() posts WM_MOUSELEAVE if conditions 1 and 2 are met, even though I'm trying to turn 
                        //mouse tracking on and the cursor is over the child window. It doesn't make sense
                        //The problems is this piece of code right here /* bMouseTracking = TrackMouseEvent(&tme); */
                        //It should turn tracking on but it doesn't it cancels it even though WS_DISABLED has already been removed
                        //at this point
                        bMouseTracking = TrackMouseEvent(&tme); 
                        InvalidateRect(hwnd, 0, TRUE);
                    }
            }
        } else {
            if(bMouseTracking) { 
                ////////If you comment everything from here ...
                TRACKMOUSEEVENT tme = { 0 };
                tme.cbSize    = sizeof(TRACKMOUSEEVENT);
                tme.dwFlags   = TME_LEAVE | TME_CANCEL;
                tme.hwndTrack = hwnd;
                //if(TrackMouseEvent(&tme)) PostMessage(hwnd, WM_MOUSELEAVE, 0, 0); //Commented this line out to do things a bit differently with the same results  
                if(TrackMouseEvent(&tme)) { //If this succeeds it means mouse tracking was canceled
                    bMouseTracking = FALSE;
                    InvalidateRect(hwnd, 0, TRUE);
                }
                ////////all the way down to here the result is the same
                //If you comment everything in this block out then you have another problem which can be tested with this condition:
                //With window enabled move mouse over window, then press the ENTER key. The color should change 
                //from red to blue but it doesn't. It will change to blue though if you move the mouse 1 or more pixels after you've pressed the ENTER key
            }
        }
        break;

    case WM_DESTROY:
        bMouseTracking = FALSE;
        break;

    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}


LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static HWND hChild;

    switch(msg)
    {
    case WM_CREATE:
        hChild = CreateWindowEx(0, g_szChildClassName, 0, WS_VISIBLE | WS_CHILD, 4, 4, 240, 80, hwnd, 0, g_hInst, 0);
        break;

    case WM_KEYDOWN:
             if(wParam == VK_SPACE)  EnableWindow(hChild, TRUE);
        else if(wParam == VK_RETURN) EnableWindow(hChild, FALSE);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default: 
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}


int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
    const TCHAR szClassName[] = L"abccccccc";
    WNDCLASSEX wc;
    HWND hwnd;
    MSG msg;

    SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
    wc.hCursor       = LoadCursor(0, IDC_ARROW);
    wc.hIcon         = LoadIcon(0, IDI_APPLICATION);
    wc.hInstance     = hInstance;
    wc.lpfnWndProc   = WndProc;
    wc.lpszClassName = szClassName;
    if(!RegisterClassEx(&wc)) return 0; //Register main window class

    SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.hCursor       = LoadCursor(0, IDC_ARROW);
    wc.hInstance     = hInstance;
    wc.lpfnWndProc   = ChildProc;
    wc.lpszClassName = g_szChildClassName;
    if(!RegisterClassEx(&wc)) return 0; //Register child window class

    g_hInst = hInstance;
    hwnd = CreateWindowEx(0, szClassName, L"Test", WS_OVERLAPPEDWINDOW, 40, 40, 420, 200, 0, 0, hInstance, 0);
    if(!hwnd) return 0;

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while(GetMessage(&msg, 0, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

编辑:您看不到图片中的光标,因为我使用了屏幕截图并且它没有捕获光标。在第一张图片中,光标在子窗口之外,在第二张图片中,光标在子窗口内

当光标在子窗口之外时按下ENTER键

在之前按下 ENTER 键并且光标悬停在子窗口上之后按下 SPACE 键

【问题讨论】:

  • 也许不是很清楚。这段代码肯定有一个错误,wParam == 0 的 WM_ENABLE 处理程序应该在禁用的窗口上调用 TrackMouseEvent(),当然也不应该调用 PostMessage。已经有一个 TrackMouseEvent() 处于活动状态,它会在窗口被禁用时自动生成 WM_MOUSELEAVE。什么都不做。
  • @HansPassant 我打电话给TrackMouseEvent()dwFlags 设置为TME_LEAVE | TME_CANCEL 以取消鼠标跟踪。可以调用TrackMouseEvent() 来设置和取消鼠标跟踪。如果鼠标正在被跟踪,窗口禁用后不会自动取消。如果在窗口被禁用后鼠标移动 1 个或更多像素,它将被取消。即使我在wParam == 0 时什么都不做,结果也是一样的。当 wParam == 0 时,将 WM_ENABLE 中的所有内容注释掉并自己查看
  • 做任何你需要做的事情,这样你就不会收到 两个 WM_MOUSELEAVE 消息。
  • WM_ENABLE 在窗口仍处于启用/禁用状态时发送,因此EnableWindow 在发送WM_ENABLE 后可能会触发WM_MOUSELEAVE。与其在 WM_ENABLE 处理程序中直接调用 TrackMouseEvent,不如向自己发布一条自定义消息并在其相应的处理程序中调用 TrackMouseEvent
  • 我尝试发布WM_MOUSEMOVE,但系统在WM_MOUSEMOVE 之后发布了WM_MOUSELEAVE。当 wParam != 0 不起作用时,似乎调用 TrackMouseEvent()WM_ENABLE 跟踪鼠标

标签: c winapi


【解决方案1】:

我不知道这是文档的缺陷还是TrackMouseEvent 中的错误,但看起来TrackMouseEvent 不希望在您的WM_ENABLE 处理程序中被调用。

为避免这种情况,请尝试从 WM_ENABLE 发布消息并从中调用 TrackMouseEvent:

case WM_ENABLE:
    PostMessage(hwnd, WM_USER, wParam, lParam);
    break;
case WM_USER:
    if(wParam) 
    {
        RECT rc;
        if(GetWindowRect(hwnd, &rc))
        {
            POINT pt;
            if(GetCursorPos(&pt))
                if(PtInRect(&rc, pt)) 
                {
                    TRACKMOUSEEVENT tme = { 0 };
                    tme.cbSize    = sizeof(TRACKMOUSEEVENT);
                    tme.dwFlags   = TME_LEAVE;
                    tme.hwndTrack = hwnd;
                    //TrackMouseEvent() posts WM_MOUSELEAVE if conditions 1 and 2 are met, even though I'm trying to turn 
                    //mouse tracking on and the cursor is over the child window. It doesn't make sense
                    //The problems is this piece of code right here /* bMouseTracking = TrackMouseEvent(&tme); */
                    //It should turn tracking on but it doesn't it cancels it even though WS_DISABLED has already been removed
                    //at this point
                    bMouseTracking = TrackMouseEvent(&tme); 
                    InvalidateRect(hwnd, 0, TRUE);
                }
        }
    } else {
        if(bMouseTracking) { 
            ////////If you comment everything from here ...
            TRACKMOUSEEVENT tme = { 0 };
            tme.cbSize    = sizeof(TRACKMOUSEEVENT);
            tme.dwFlags   = TME_LEAVE | TME_CANCEL;
            tme.hwndTrack = hwnd;
            //if(TrackMouseEvent(&tme)) PostMessage(hwnd, WM_MOUSELEAVE, 0, 0); //Commented this line out to do things a bit differently with the same results  
            if(TrackMouseEvent(&tme)) { //If this succeeds it means mouse tracking was canceled
                bMouseTracking = FALSE;
                InvalidateRect(hwnd, 0, TRUE);
            }
            ////////all the way down to here the result is the same
            //If you comment everything in this block out then you have another problem which can be tested with this condition:
            //With window enabled move mouse over window, then press the ENTER key. The color should change 
            //from red to blue but it doesn't. It will change to blue though if you move the mouse 1 or more pixels after you've pressed the ENTER key
        }
    }
    break;

【讨论】:

  • 还是不行。系统在WM_USER之后发布WM_MOUSELEAVE
猜你喜欢
  • 2018-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-05
  • 2017-08-22
  • 1970-01-01
相关资源
最近更新 更多