бесконечный цикл сообщений Windows - C ++

0

Я попытался создать этот класс окна, который создает и показывает окно. Когда я запускаю этот класс, GetMessage продолжает отправлять сообщение WM_PAINT, а диспетчер задач показывает мне примерно 50% использования ЦП только из моего процесса.

main.cpp:

#include "Window.h"

int WINAPI WinMain(
    HINSTANCE /* hInstance */,
    HINSTANCE /* hPrevInstance */,
    LPSTR /* lpCmdLine */,
    int /* nCmdShow */
    )
{
    Window::GlobalInitialize();

    Window window(L"abcd", 500, 500);

    if (SUCCEEDED(window.Initialize()))
        window.RunMessageLoop();

    Window::GlobalTerminate();

    return 0;
}

window.h:

#ifndef WINDOW_HEADER
#define WINDOW_HEADER

#include <Windows.h>

#include <functional>

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

class Window;

typedef std::function<void(Window *window, UINT id, LPCWSTR message)> ErrorCallback;

class Window {
public:
#define ERROR_FAILED_HINSTANCE 1
#define ERROR_FAILED_HINSTANCE_STR L"Failed to retrieve hInstance"

#define ERROR_FAILED_REGISTER 2
#define ERROR_FAILED_REGISTER_STR L"Failed to register window class"

#define ERROR_FAILED_CREATION 3
#define ERROR_FAILED_CREATION_STR L"Failed to create window"

    typedef std::function<HRESULT(Window *window)> WEOnCreate;
    typedef std::function<HRESULT(Window *window)> WEOnDestroy;
    typedef std::function<HRESULT(Window *window)> WEOnRender;
    typedef std::function<HRESULT(Window *window, UINT width, UINT height)> WEOnResize;
    typedef std::function<HRESULT(Window *window, UINT horizontalResolution, UINT verticalResolution)> WEOnScreenResolutionChange;

    Window(LPCWSTR title, UINT width, UINT height);
    ~Window();

    HRESULT SetSize(UINT width, UINT height);
    HRESULT SetTitle(LPCWSTR title);

    inline UINT GetWidth() { return _width; }
    inline UINT GetHeight() { return _height; }
    inline LPCWSTR GetTitle() { return _title; }
    inline HWND GetHandle() { return hWnd; }

    inline void SetOnCreateCallback(WEOnCreate fun) { _onCreate = fun; }
    inline void SetOnDestroyCallback(WEOnDestroy fun) { _onDestroy = fun; }
    inline void SetOnRenderCallback(WEOnRender fun) { _onRender = fun; }
    inline void SetOnResizeCallback(WEOnResize fun) { _onResize = fun; }
    inline void SetOnScreenResolutionChangeCallback(WEOnScreenResolutionChange fun) { _onResChange = fun; }

    inline void SetExtraAllocatedSpace(void *ptr) { extra = ptr; }
    inline void *GetExtraAllocatedSpace() { return extra; }

    inline void Terminate() { if (hWnd) DestroyWindow(hWnd); }

    static inline void SetErrorCallback(ErrorCallback fun) { _errorCallback = fun; }

    HRESULT Initialize();
    void RunMessageLoop();

    static HRESULT GlobalInitialize();
    static HRESULT GlobalTerminate();
private:
    static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

    static inline void throwError(Window *window, UINT id, LPCWSTR message) {
        if (_errorCallback)
            _errorCallback(window, id, message);
    }

    WEOnCreate _onCreate;
    WEOnDestroy _onDestroy;
    WEOnRender _onRender;
    WEOnResize _onResize;
    WEOnScreenResolutionChange _onResChange;

    static ErrorCallback _errorCallback;
    static LPCWSTR szClassName;
    static HINSTANCE hInstance;

    HWND hWnd;
    int _width, _height;
    LPCWSTR _title;
    void *extra;
};

#endif

window.cpp:

#include "Window.h"
//Initialize static variables
ErrorCallback Window::_errorCallback = nullptr;
LPCWSTR Window::szClassName = L"WindowClass";
HINSTANCE Window::hInstance;

Window::Window(LPCWSTR title = L"Window", UINT width = 640, UINT height = 480) :
_onCreate(nullptr),
_onDestroy(nullptr),
_onRender(nullptr),
_onResize(nullptr),
hWnd(NULL),
extra(NULL), 
_width(width),
_height(height),
_title(title) {}

Window::~Window() {
    if (hWnd) {
        DestroyWindow(hWnd);
        hWnd = NULL;
    }
}

HRESULT Window::GlobalInitialize() {
    // Retreive hInstance
    hInstance = GetModuleHandle(NULL);
    if (!hInstance) {
        throwError(NULL, ERROR_FAILED_HINSTANCE, ERROR_FAILED_HINSTANCE_STR);
        return E_FAIL;
    }

    // Create window class
    WNDCLASSEX wcex = {};
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = Window::WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = sizeof(LONG_PTR);
    wcex.hInstance = hInstance;
    wcex.hbrBackground = NULL;
    wcex.lpszMenuName = NULL;
    wcex.hCursor = LoadCursor(NULL, IDI_APPLICATION);
    wcex.lpszClassName = szClassName;

    if (!RegisterClassEx(&wcex)) {
        throwError(NULL, ERROR_FAILED_REGISTER, ERROR_FAILED_REGISTER_STR);
        return E_FAIL;
    }

    return S_OK;
}

HRESULT Window::GlobalTerminate() {
    if (UnregisterClass(szClassName, hInstance))
        return S_OK;
    else
        return E_FAIL;
}

void Window::RunMessageLoop() {
    MSG msg;

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

HRESULT Window::Initialize() {
    // Create the window
    hWnd = CreateWindow(
        szClassName,
        _title,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        _width,
        _height,
        NULL,
        NULL,
        hInstance,
        this
        );

    if (!hWnd) {
        throwError(this, ERROR_FAILED_CREATION, ERROR_FAILED_CREATION_STR);
        return E_FAIL;
    }

    // Show and render the window
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);

    return S_OK;
}

LRESULT CALLBACK Window::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    if (message == WM_CREATE) {
        LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
        Window *window = (Window *)pcs->lpCreateParams;
        ::SetWindowLongPtr(hWnd, GWLP_USERDATA, PtrToUlong(window));

        if (window->_onCreate != nullptr)
            window->_onCreate(window);


    }

    Window *pWnd = reinterpret_cast<Window *>(static_cast<LONG_PTR>(GetWindowLongPtr(hWnd, GWLP_USERDATA)));
    HRESULT hr = S_OK;

    if (!pWnd) {
        return DefWindowProc(hWnd, message, wParam, lParam);
    }

    switch (message) {
    case WM_PAINT:
    {
                     if (pWnd->_onRender)
                         hr = pWnd->_onRender(pWnd);
                     else
                         hr = S_OK;
    }
        break;
    case WM_SIZE:
    {
                    if (pWnd->_onResize)
                        hr = pWnd->_onResize(pWnd, LOWORD(lParam), HIWORD(lParam));
                    else
                        hr = S_OK;
    }
        break;
    case WM_DISPLAYCHANGE:
    {
                    if (pWnd->_onResChange)
                        hr = pWnd->_onResChange(pWnd, LOWORD(lParam), HIWORD(lParam));
                    else
                        hr = S_OK;
    }
        break;
    case WM_DESTROY:
    {
                    if (pWnd->_onDestroy && FAILED(pWnd->_onDestroy(pWnd)))
                        break;
    }
        PostQuitMessage(0);
        hWnd = NULL;
        break;
    default:
        hr = DefWindowProc(hWnd, message, wParam, lParam);
    }

    return hr;
}

HRESULT Window::SetSize(UINT width, UINT height) {
    if (hWnd)
    if (!::SetWindowPos(hWnd, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER))
        return E_FAIL;

    _width = width;
    _height = height;

    return S_OK;
}

HRESULT Window::SetTitle(LPCWSTR title) {
    if (hWnd)
    if (!::SetWindowText(hWnd, title))
        return E_FAIL;

    _title = title;

    return S_OK;
}

Надеюсь, кто-то может мне помочь, так как все выглядит нормально (окно даже отлично работает).

Теги:
winapi
infinite-loop

1 ответ

3

Во-первых, вы, похоже, обрабатываете оконную процедуру, как если бы это был метод COM, но оконные процедуры не возвращают HRESULT - они возвращают LRESULT, значение которого отличается для каждого сообщения.

В случае WM_PAINT невозможно вернуть значение, которое указывает "Мне не нужно рисовать на этот раз". Система будет отправлять сообщения WM_PAINT до тех пор, пока часть вашего окна будет отмечена как грязная, и то, как вы отмечаете грязную область как "окрашенную", - это вызвать BeginPaint и EndPaint. Если вы этого не сделаете, система будет продолжать считать ваше окно грязным и продолжать отправлять сообщения WM_PAINT.

Вы не _onRender исходный код для вашей функции _onRender но тот факт, что вы сделали обработку WM_PAINT необязательной (т. SetOnRenderCallback Если ничто не вызывает SetOnRenderCallback то никакой обратный вызов не будет зарегистрирован) означает, что вы, вероятно, не обрабатываете WM_PAINT правильно. По крайней мере, если вы сами не рисуете картину, вы должны передать сообщение в DefWindowProc чтобы разрешить обработку по умолчанию.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню