Пользовательское сообщение WM_APP не публикуется в очереди сообщений - PullRequest
0 голосов
/ 31 октября 2019

Я пытаюсь отправить пользовательское сообщение (WM_APP + 1), когда WM_SIZE отправляется в оконную процедуру. Я хочу быть в состоянии поймать его из другой функции с помощью PeekMessage и сделать что-то. Но когда я проверяю это, кажется, что сообщения не отправляются в очередь. Добавление некоторых операторов printf показывает мне, что это идет к оконной процедуре. Странная вещь в том, что когда я выполняю код в отладчике, он работает нормально, но когда я работаю нормально, он возвращается к неработающему.

Работа примера программы с проблемой, измените размер окна для проверки:

#include <windows.h>
#include <stdio.h>

#pragma comment(lib, "user32.lib")

#define OBG_EVENT_QUIT 0
#define OBG_EVENT_RESIZE 1
#define OBG_EVENT_NO -1
#define OBG_EVENT_UNKNOWN -2

//user defined event
#define OBG_WM_RESIZE (WM_APP + 1)

typedef union
{
    int type;

    struct
    {
        int type;
        int width;
        int height;

    } resizeEvent;

} obg_event;

LRESULT CALLBACK obgpf_DefaultWindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT result = 0;

    switch(message)
    {
        case WM_CLOSE:
        {
            PostMessageA(window, WM_QUIT, 0, 0);
        } break;

        //this should be handled by OBGGetEvent
        case OBG_WM_RESIZE:
        {
            printf("MESSAGE WENT THROUGH. DON'T WANT THIS\n");
        } break;

        case WM_SIZE:
        {
            PostMessageA(window, OBG_WM_RESIZE, wParam, lParam);
        } break;

        default:
        {
            result = DefWindowProc(window, message, wParam, lParam);
        } break;
    }

    return result;
}

int OBGGetEvent(obg_event *event)
{
    int moreMessages = 0;
    MSG message;

    if(PeekMessage(&message, 0, 0, 0, PM_REMOVE))
    {
        moreMessages = 1;
        switch(message.message)
        {
            case WM_QUIT:
            {
                event->type = OBG_EVENT_QUIT;
            } break;


            case OBG_WM_RESIZE:
            {
                event->type = OBG_EVENT_RESIZE;
                event->resizeEvent.type = OBG_EVENT_RESIZE;
                event->resizeEvent.width = LOWORD(message.lParam);
                event->resizeEvent.height = HIWORD(message.lParam);
            } break;

            default:
            {
                event->type = OBG_EVENT_UNKNOWN;
                TranslateMessage(&message);
                DispatchMessage(&message);
            } break;
        }
    }
    else
    {
        event->type = OBG_EVENT_NO;
    }

    return moreMessages;
}

int main()
{
    HINSTANCE instance = GetModuleHandleA(0);

    WNDCLASSEX windowClass = {0};
    windowClass.cbSize = sizeof(windowClass);
    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    windowClass.lpfnWndProc = obgpf_DefaultWindowCallback;
    windowClass.hInstance = instance;
    windowClass.lpszClassName = "testClass";
    windowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
    windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    windowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
    windowClass.hCursor = LoadCursorA(0, IDC_ARROW);

    HWND window;

    if(RegisterClassEx(&windowClass))
    {
        window = CreateWindowEx(0,
                                windowClass.lpszClassName,
                                "test window",
                                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                                CW_USEDEFAULT,
                                CW_USEDEFAULT,
                                500,
                                300,
                                0,
                                0,
                                instance,
                                0);

        if(window)
        {
            int appIsRunning = 1;
            obg_event event = {0};
            event.type = -1;
            while(appIsRunning)
            {
                while(OBGGetEvent(&event))
                {
                    if(event.type == OBG_EVENT_QUIT)
                    {
                        printf("event quit\n");
                        appIsRunning = 0;
                        break;
                    }
                    else if(event.type == OBG_EVENT_RESIZE)
                    {
                        printf("window resized: width %d height %d\n", event.resizeEvent.width, event.resizeEvent.height);
                    }
                }

                Sleep(33);
            }
        }
        else
        {
            printf("window error\n");
        }
    }
    else
    {
        printf("windowClass error\n");
    }

    return 0;
}

Я попытался сделать это с SendMessage вместо PeekMessage, но произошло то же самое. Не уверен, что я пропускаю или неправильно понимаю, но любая помощь приветствуется!

РЕДАКТИРОВАТЬ: добавлена ​​полная рабочая программа, которая воспроизводит проблему

1 Ответ

2 голосов
/ 31 октября 2019

Спасибо за пример кода.

Поведение, которое вы видите, заключается в том, что при изменении размера окна с помощью мыши ОС вводит модальный цикл обработки сообщений для обработки ввода с помощью мыши. Во время этого цикла ваш цикл сообщений не запускается - это означает, что ваш PeekMessage() и специальная обработка сообщений также не запускаются. Вместо этого сообщения просто отправляются как обычно для вашей оконной процедуры.

Есть два решения, которые приходят на ум сразу, но то, как вы справляетесь с этим, действительно зависит от дизайна вашей программы и того, почему вы хотите обрабатывать события размератаким образом.

Первая идея, которая у меня возникла, заключается в том, чтобы отслеживать, находитесь ли вы в модальном цикле определения размера или нет, и отложить публикацию уведомления до завершения цикла. Пример того, как сделать это, используя предоставленную вами оконную процедуру, приведен ниже.

Второе решение - просто вызывать обработчик событий изменения размера напрямую, когда вы получаете WM_SIZE (или, если вам нужно пройти через систему событийпоместите обработчик для него в оконную процедуру вместо использования PostMessage).

Пример кода для первого предложения:

LRESULT CALLBACK obgpf_DefaultWindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT result = 0;
    static bool fResized = false;
    static bool fInSizeLoop = false;

    switch(message)
    {
        case WM_CLOSE:
        {
            PostMessageA(window, WM_QUIT, 0, 0);
        } break;

        //this should be handled by OBGGetEvent
        case OBG_WM_RESIZE:
        {
            printf("MESSAGE WENT THROUGH. DON'T WANT THIS\n");
        } break;

        case WM_SIZE:
        {
            if (fInSizeLoop) // in modal size loop, defer notification
                fResized = true;
            else
                PostMessageA(window, OBG_WM_RESIZE, wParam, lParam);
        } break;

        case WM_ENTERSIZEMOVE:
            fInSizeLoop = true; // begin modal size loop
            break;

        case WM_EXITSIZEMOVE:
            fInSizeLoop = false; // left modal size loop
            // post resize notification now
            if (fResized) {
                RECT rc;
                GetClientRect(window, &rc);
                PostMessageA(window, OBG_WM_RESIZE, 0, MAKELPARAM(rc.right - rc.left, rc.bottom - rc.top));
                fResized = false;
            }
            break;

        default:
        {
            result = DefWindowProc(window, message, wParam, lParam);
        } break;
    }

    return result;
}

(извините, это код C ++, и я простозаметил, что вы пометили как C - но принцип тот же).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...