WinAPI WNDPROC to Engine/Game MessageHandler

Started by
4 comments, last by Renthalkx97 8 years, 2 months ago

Hello all, I need some opinions from everyone. I've been questioning my approach on handling window events. As you most likely know, the window procedure is very tightly coupled to a winapi window, as is registering the window class.

The way I circumvent this is by using a static MessageRouter(read static WNDPROC) that grabs the pointer to my engine class that is stored in the extra user data of the window. I then call my MessageHandler from my engine inside the MessageRouter and that is how I forward my events.

I do feel as though this is a very suitable solution, however I also feel as though there are better, more elegant ways to handle this. I just wanted to get everyone's input on how they handle this exact problem.

This is an example of my my approach looks;


LRESULT CALLBACK LWindowWin32::MessageRouter(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	Lightning *pLightning;

	if (msg == WM_NCCREATE)
	{
		LPCREATESTRUCT lpCreateStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);

		pLightning = static_cast<Lightning*>(lpCreateStruc->lpCreateParams);

		SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pLightning));
	}
	else
	{
		pLightning = reinterpret_cast<Lightning*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
	}

	if (pLightning)
	{
		pLightning->MessageHandler(/*converted winapi parameters would go here*/)
	}

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

Thank you,

BagelBytes

Advertisement

If anyone is interested in a solution to the aforementioned issue, here is a link to an Ideone that Washu provided for me to reference: http://ideone.com/E8scoR

Thank you Washu and thank you also Hodgman!

What do you feel isn't elegant about it?

Another way is to capture messages in the main loop when returned from GetMessage/PeekMessage.

One thing I see is that you should probably not always return DefWindowProc at the end, but let 'MessageHandler' decide. Though I'm not entirely clear on what your message handler does, do you pass everything to it or where do you decide what "converted winapi parameters" are?

Another way is to capture messages in the main loop when returned from GetMessage/PeekMessage.

This is in general not an option, since Windows sometimes uses an internal message loop instead of the applications loop, e.g. when a menu is opened, the window is resized, when a modal dialog is shown...

I do something quite similar. I have a Window class that stores a pointer to itself in the extra user data, and a static message handler that just forwards to the appropriate class. I inherit the Window class whenever I need a window (usually I only need one) and override WMProc(). All the Window's share the same WNDCLASSEX (well there are ways to user other WNDCLASS's but the default one is shared). It seems easy and clean enough for me.

One this I do a bit differently is I set the extra data right after I create the window:
hwnd = CreateWindowEx(...);
if (hwnd == nullptr) throw_detailed(WindowsException() << "CreateWindowEx() error, cannot create window.");

SetLastError(0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
if (GetLastError() != 0) throw_detailed(WindowsException() << "SetWindowLongPtr() error, cannot set window pointer.");


What do you feel isn't elegant about it?

I just felt as though I could better decouple my event handling from window, but the in all honesty, I'm just prematurely optimizing if you even want to call it that. With the approach Washu offered me, I am a lot more satisfied with the overall architecture of my whole event system. It just feels alot cleaner to me than what I was doing. The above code wasn't my actual code, but an example. The converted winapi parameters comment would be the message id that would be mapped to the winapi equivalent.

Nonetheless, I don't believe there is one correct way to do it, all of the solutions mentioned here work extremely well. I'm currently refactoring Washu's example to work with classes and more cleanly interface with my existing setup.

I do something quite similar. I have a Window class that stores a pointer to itself in the extra user data, and a static message handler that just forwards to the appropriate class. I inherit the Window class whenever I need a window (usually I only need one) and override WMProc(). All the Window's share the same WNDCLASSEX (well there are ways to user other WNDCLASS's but the default one is shared). It seems easy and clean enough for me.

One this I do a bit differently is I set the extra data right after I create the window:


hwnd = CreateWindowEx(...);
if (hwnd == nullptr) throw_detailed(WindowsException() << "CreateWindowEx() error, cannot create window.");

SetLastError(0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
if (GetLastError() != 0) throw_detailed(WindowsException() << "SetWindowLongPtr() error, cannot set window pointer.");

When I first learned about this method, I had seen them always handling it inside of the NCCREATE message so I just went with that. Your way obviously works too and I'm sure it cleans up your static wndproc a bit, not having to set the user data.

This topic is closed to new replies.

Advertisement