如何在裸事件循环中处理WM_NCHITTEST?

时间:2017-08-29 19:22:31

标签: c winapi

我正在玩Windows API试图理解它的行为,我意识到我可以完全删除WNDPROC并使用裸事件循环处理事情,如下所示:

#include <Windows.h>

static struct {
    HWND desktop;
    HWND window;
} global;

int main(int argc, char **argv)
{
    /* anonymous scope: register window class */
    {
        WNDCLASSEXW wcx;
        wcx.cbSize = sizeof(wcx);
        wcx.style = CS_HREDRAW | CS_VREDRAW;
        wcx.lpfnWndProc = DefWindowProc;
        wcx.cbClsExtra = sizeof(void *);
        wcx.cbWndExtra = sizeof(void *);
        wcx.hInstance = (HINSTANCE)GetModuleHandle(NULL);
        wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
        wcx.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME;
        wcx.lpszMenuName = NULL;
        wcx.lpszClassName = L"MyWindow";
        wcx.hIconSm = wcx.hIcon;
        RegisterClassExW(&wcx);
    }

    global.desktop = GetDesktopWindow();

    global.window = CreateWindowExW (
        0,
        L"MyWindow",
        NULL,
        WS_POPUPWINDOW | WS_VISIBLE,
        0,
        0,
        320,
        200,
        global.desktop,
        NULL,
        (HINSTANCE)GetModuleHandle(NULL),
        NULL
    );

    /* anonymous scope, event loop */
    {
        MSG msg;

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

            if(msg.hwnd == global.window) {
                if (msg.message == WM_PAINT) {
                    PAINTSTRUCT ps;
                    HDC hdc;

                    hdc = BeginPaint(msg.hwnd, &ps);

                    SelectObject(hdc, GetStockObject(BLACK_BRUSH));
                    Rectangle(hdc, 0, 0, 320, 200);

                    EndPaint(msg.hwnd, &ps);
                } else {
                    DispatchMessage(&msg);
                }
            } else {
                DispatchMessage(&msg);
            }
        }    
    }


    return 0;
}

我想更进一步尝试使用this技术使这个窗口可移动,并且因为我不能以我习惯的方式从消息循环“返回”而感到困惑(声明return hit;}在这种情况下没有意义。

这是我开始的方式,并且感到困惑:

#include <Windows.h>

static struct {
    HWND desktop;
    HWND window;
} global;

int main(int argc, char **argv)
{
    /* anonymous scope: register window class */
    {
        WNDCLASSEXW wcx;
        wcx.cbSize = sizeof(wcx);
        wcx.style = CS_HREDRAW | CS_VREDRAW;
        wcx.lpfnWndProc = DefWindowProc;
        wcx.cbClsExtra = sizeof(void *);
        wcx.cbWndExtra = sizeof(void *);
        wcx.hInstance = (HINSTANCE)GetModuleHandle(NULL);
        wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
        wcx.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME;
        wcx.lpszMenuName = NULL;
        wcx.lpszClassName = L"MyWindow";
        wcx.hIconSm = wcx.hIcon;
        RegisterClassExW(&wcx);
    }

    global.desktop = GetDesktopWindow();

    global.window = CreateWindowExW (
        0,
        L"MyWindow",
        NULL,
        WS_POPUPWINDOW | WS_VISIBLE,
        0,
        0,
        320,
        200,
        global.desktop,
        NULL,
        (HINSTANCE)GetModuleHandle(NULL),
        NULL
    );

    /* anonymous scope, event loop */
    {
        MSG msg;

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

            if(msg.hwnd == global.window) {
                if (msg.message == WM_PAINT) {
                    PAINTSTRUCT ps;
                    HDC hdc;

                    hdc = BeginPaint(msg.hwnd, &ps);

                    SelectObject(hdc, GetStockObject(BLACK_BRUSH));
                    Rectangle(hdc, 0, 0, 320, 200);

                    EndPaint(msg.hwnd, &ps);
                } else if (msg.message == WM_NCHITTEST) {
                    LRESULT hit = DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
                    if (hit == HTCLIENT) {
                        hit = HTCAPTION;
                    }
                    // return hit; // makes no sense here
                } else {
                    DispatchMessage(&msg);
                }
            } else {
                DispatchMessage(&msg);
            }
        }    
    }


    return 0;
}

如何模拟从WM_NCHITTEST条件返回“命中”,以便它像在解决方案中一样移动窗口:https://stackoverflow.com/a/7773941/2012715

PS:我知道最好使用地图(比如std :: unordered_map),而不是使用long if / switch来实现可扩展性和可读性,但我想让这个例子更直接。

1 个答案:

答案 0 :(得分:4)

你不能。

您展示的代码永远不会有效,因为(Get|Peek)Message()仅返回QUEUED条消息。您无法回复排队的邮件,因为发件人不等待回复,它将邮件放入队列并转移到其他内容。只能回复使用SendMessage...()系列函数发送的NON-QUEUED条消息。 (Get|Peek)Message()将永远不会返回已发送的消息,但会在内部将其分派到目标窗口的消息过程(仅调度从另一个线程发送的消息,由拥有该消息的同一线程发送到窗口的消息窗口将完全绕过消息队列。

WM_PAINT是一个排队的消息,因此您的事件循环会看到它。但是WM_NCHITTEST没有排队,所以你的消息循环永远不会直接看到它,它只能在消息过程中看到。

您所展示的不是处理Windows UI消息循环的正确方法。由于您正在创建UI窗口,因此必须为其提供消息过程(如果不是RegisterClass/Ex(),则为SetWindowLong/Ptr(GWL_WNDPROC)SetWindowSubclass())。但是,如果您需要手动处理消息,请不要使用DefWindowProc()作为该过程。提供您自己的消息流程,针对任何未处理的消息调用DefWindowProc()(如果是CallWindowProc(),则为GWL_WNDPROC,或DefSubclassProc()SetWindowSubclass()),例如:

LRESULT WINAPI MyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) {
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            SelectObject(hdc, GetStockObject(BLACK_BRUSH));
            Rectangle(hdc, 0, 0, 320, 200);

            EndPaint(hwnd, &ps);
            return 0;
        }

        case WM_NCHITTEST: {
            LRESULT hit = DefWindowProc(hwnd, uMsg, wParam, lParam);
            if (hit == HTCLIENT) {
                hit = HTCAPTION;
            }
            return hit;
        }
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int main(int argc, char **argv)
{
    /* anonymous scope: register window class */
    {
        WNDCLASSEXW wcx;
        wcx.cbSize = sizeof(wcx);
        wcx.style = CS_HREDRAW | CS_VREDRAW;
        wcx.lpfnWndProc = MyWndProc; // <--
        wcx.cbClsExtra = sizeof(void *);
        wcx.cbWndExtra = sizeof(void *);
        wcx.hInstance = (HINSTANCE)GetModuleHandle(NULL);
        wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
        wcx.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME;
        wcx.lpszMenuName = NULL;
        wcx.lpszClassName = L"MyWindow";
        wcx.hIconSm = wcx.hIcon;
        RegisterClassExW(&wcx);
    }

    global.desktop = GetDesktopWindow();

    global.window = CreateWindowExW (
        0,
        L"MyWindow",
        NULL,
        WS_POPUPWINDOW | WS_VISIBLE,
        0,
        0,
        320,
        200,
        global.desktop,
        NULL,
        (HINSTANCE)GetModuleHandle(NULL),
        NULL
    );

    /* anonymous scope, event loop */
    {
        MSG msg;

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

    return 0;
}