Windows message loop questions

Started by
4 comments, last by cdoubleplusgood 10 years, 6 months ago

I have a few questions regarding the Windows procedure and how to properly manage messages.

1) What's the difference between handling messages in the example:


while(!exitGame)
    {
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);

			switch(msg.message)
			{
			case WM_KEYDOWN:
				keyboard.UpdateKey(msg.wParam, true);
				break;
			}
        }

Compared to handling it in the WindowProc method


LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_KEYDOWN:
             keyboard.UpdateKey(msg.wParam, true);
	     break;

    }

    return DefWindowProc (hWnd, message, wParam, lParam);
}

My first guess is that in the WindowProc is that it handles it per-window?

2) For an application that's only dealing with 1 window is it okay to just ignore the WindowProc method? My main problem with this is I would like to update my Keyboard input class from within the function, is the only way to get around this to make it a global?

Thanks smile.png

Advertisement
Message handling should be handled in the window message handler (which is what DispatchMessage DOES). The only real exception to this is detecting WM_QUIT with PeekMessage. In all other cases you should pass it off to the window message handler. Not handling the messages in the handler is buggy, as there are several calls that can be made which directly invoke the message handler WITH THE EXPECTATION that it will handle the message. A prime example is SendMessage within the same thread.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Thanks!

2) For an application that's only dealing with 1 window is it okay to just ignore the WindowProc method? My main problem with this is I would like to update my Keyboard input class from within the function, is the only way to get around this to make it a global?

No. See GWLP_USERDATA and WM_NCCREATE along with the final parameter of the CreateWindowEx function.

As an alternative, there's always WTL, which handles your window creation and management quite cleanly and simply.
#include <iostream>
#include <atlbase.h>
#include <atlwin.h>

class MainWindow : public CWindowImpl<MainWindow, CWindow, CFrameWinTraits>
{
public:
    MainWindow() {
        Create(nullptr, 0, _T("Sample Window"));
        ShowWindow(SW_SHOW);
    }

    BEGIN_MSG_MAP(MainWindow)
        MESSAGE_HANDLER(WM_DESTROY, OnDestroy);
        MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown);
        MESSAGE_HANDLER(WM_KEYUP, OnKeyUp);
    END_MSG_MAP()

private:
    LRESULT OnKeyDown(UINT MSG, WPARAM wParam, LPARAM lParam, BOOL& handled) {
        if(!(HIWORD(lParam) & KF_REPEAT)) {
            std::cout<<"Key down: "<<std::hex<<wParam<<std::endl;
            // ...
        }
        return 0;
    }

    LRESULT OnKeyUp(UINT MSG, WPARAM wParam, WPARAM lParam, BOOL& handled) {
        std::cout<<"Key up: "<<std::hex<<wParam<<std::endl;
        // ...
        return 0;
    }

    LRESULT OnDestroy(UINT msg, WPARAM wParam, WPARAM lParam, BOOL& handled) {
        PostQuitMessage(0);
        return 0;
    }
};

int main() {
    MainWindow w;

    MSG message;
    while(true) {
        if(PeekMessage(&message, 0, 0, 0, PM_REMOVE)) {
            if(message.message == WM_QUIT)
                break;

            TranslateMessage(&message);
            DispatchMessage(&message);
        } else {
        }
    }
}

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

I really like the look of WTL, looks much cleaner!

So I've been looking into things and found that during the window creation you can allocate extra bytes during the window creation with WNDCLASSEX.cbWndExtra. where you then create your window you can pass a pointer from CreateWindowEx() through to the lparam of the WM_CREATE message and store that pointer using SetWindowLongPtr. Then every time the in the message loop you can get that pointer with the specified HWND... Is this correct way of doing this? Example:

Thanks again for the help smile.png


LRESULT CALLBACK Window::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
         Window* window = (Window*)GetWindowLongPtr(hWnd, 0);
		
         switch(message)
	{
                case WM_CREATE:
		{
		    CREATESTRUCT* cs = (CREATESTRUCT*) lParam;
	            SetWindowLongPtr(hWnd, 0, (LONG_PTR) cs->lpCreateParams;
		} break;
        }
}

void Window::Create()
{
        ZeroMemory(&wc, sizeof(WNDCLASSEX));

	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = sizeof(Window*); //Allocate extra space for a pointer to the Window
	wc.hInstance = hInstance;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
		
	wc.lpszClassName = "GameClass";

        RegisterClassEx(&wc);

	RECT wr = {0, 0, windowWidth, windowHeight};
	AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);

	hWnd = CreateWindowEx(NULL,
		"GameClass",
		windowTitle.c_str(),
		WS_OVERLAPPEDWINDOW,
		300,
		100,
		wr.right - wr.left,
		wr.bottom - wr.top,
		NULL,
		NULL,
		hInstance,
		this); //Pass a pointer to this to WM_CREATE
}

One more reason to not handle the messages in the message loop is this: In several situations the user-implemented message loop is not used. E.g. when showing a modal dialog (incl. message boxes), when moving or resizing a window, when showing a menu, Windows uses an internal loop that dispatches the messages.

This topic is closed to new replies.

Advertisement