infine windows消息循环 - c ++

时间:2013-12-15 07:55:06

标签: c++ windows winapi infinite-loop

我尝试创建这个创建并显示窗口的窗口类。当我运行这个类时,GetMessage继续发送WM_PAINT消息,任务管理器向我显示我的进程中大约50%的CPU使用率。

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;
}

我希望有人可以帮助我,因为一切看起来都不错(窗口运行正常)。

1 个答案:

答案 0 :(得分:4)

首先,您似乎将窗口过程视为COM方法,但窗口过程不返回HRESULT - 它们返回LRESULT,其含义因每条消息而异。

WM_PAINT的情况下,无法返回表示“我不需要绘制此时间”的值。只要您的窗口的一部分被标记为脏,系统就会发送WM_PAINT消息,并且您将脏区标记为“已绘制”的方式是调用BeginPaintEndPaint。如果您不这样做,系统将继续将您的窗口视为脏,并继续发送WM_PAINT消息。

您尚未显示_onRender函数的源代码,但事实上您已将WM_PAINT处理为可选(即,如果没有调用SetOnRenderCallback则不会注册回调)表示您可能没有正确处理WM_PAINT。至少,如果您不自己进行绘画,则应将消息传递到DefWindowProc以允许进行默认处理。