代码之家  ›  专栏  ›  技术社区  ›  Mike32ab

当我按下控制键时滚动鼠标滚轮时,为什么应用程序会无故终止?

  •  3
  • Mike32ab  · 技术社区  · 10 年前

    当控制键按下,然后我滚动鼠标滚轮时,应用程序会无故终止。我正在Windows XP上测试这个。只有在滚动时按下控制键时才会发生这种情况。如果滚动时未按下控制键,则不会发生这种情况。不知道其他操作系统的情况如何。使用以下代码对此进行测试

    #include <windows.h>
    #include <tchar.h>
    
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        static HWND hEdit = 0;
    
        switch(msg)
        {
        case WM_CREATE:
            hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("Edit"), 0, WS_VISIBLE | WS_CHILD | WS_HSCROLL | WS_VSCROLL | ES_MULTILINE | ES_READONLY,
                0, 0, 0, 0, hwnd, 0, GetModuleHandle(0), 0);
            break;
    
        case WM_SIZE:
            MoveWindow(hEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
            break;
    
        case WM_MOUSEWHEEL:
            SendMessage(hEdit, msg, wParam, lParam);
            break;
    
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
        }
        return 0;
    }
    
    int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
    {
        WNDCLASSEX wc = {0};
        HWND hwnd;
        MSG msg;
    
        wc.cbSize = sizeof wc;
        wc.hbrBackground = 0;
        wc.hCursor = LoadCursor(0, IDC_ARROW);
        wc.hIcon = LoadIcon(0, IDI_APPLICATION);
        wc.hInstance = hInstance;
        wc.lpfnWndProc = WndProc;
        wc.lpszClassName = TEXT("MainClass");
    
        if(!RegisterClassEx(&wc)) 
            return 0;
    
        hwnd = CreateWindowEx(0, wc.lpszClassName, TEXT("Hello"), WS_OVERLAPPEDWINDOW, 40, 20, 400, 200,
            0, 0, hInstance, 0);
    
        if(!hwnd) 
            return 0;
    
        ShowWindow(hwnd, nCmdShow);
    
        while(GetMessage(&msg, 0, 0, 0) > 0)
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return (int)msg.wParam;
    }
    

    如果我注释掉下面的3行,并且在滚动时按下控制键,则不会发生这种情况

    case WM_MOUSEWHEEL:
        SendMessage(hEdit, msg, wParam, lParam);
        break;
    
    1 回复  |  直到 10 年前
        1
  •  6
  •   Jonathan Potter    10 年前

    扩展我在评论中所说的内容:

    1. 您的窗口类转发 WM_MOUSEWHEEL 到编辑控件
    2. 如果按下控制键,编辑控件将忽略 WM_使用轮子 消息并将其传递给 DefWindowProc .
    3. 窗口过程 将消息传递到父链(此行为记录在 WM_MOUSEWHEEL documentation ).
    4. 您的窗口接收转发的消息并返回到步骤#1

    最终,堆栈用完,进程终止。

    有三种方法可以解决此问题:

    第一种(可能也是最安全的)是使用标志来防止递归循环;例如。:

    static bool fInForwardMsg; // if you have multiple windows you would want to make this a local variable
    
    case WM_MOUSEWHEEL:
        if (!fInForwardMsg) {
            fInForwardMsg = true;
            SendMessage(hEdit, uMsg, wParam, lParam);
            fInForwardMsg = false;
        }
        break;
    

    第二种解决方案依赖于编辑控件查看 wParam 值,以查看控制键是否按下(顺便说一下,它还检查换档)。这是内部未记录的行为,可能会发生变化,因此您不应该依赖它,但您应该能够通过不转发 wParam参数 。例如:

    case WM_MOUSEWHEEL:
        SendMessage(hEdit, msg, wParam & ~0xffff, lParam);
        break;
    

    第三种解决方案也是最简单的;由于如果按住shift或control,编辑控件实际上不会执行任何操作,因此在这些情况下,请不要转发消息:

    case WM_MOUSEWHEEL:
        if (!(wParam & (MK_SHIFT | MK_CONTROL))
            SendMessage(hEdit, msg, wParam, lParam);
        break;