WndProc() as a class member (Win32)

Sometimes, when a Window is declared in its own class, it is useful to associate other members to it and modify them or calling other methods from the WndProc function (for instance after pushing a button) . Nevertheless, since the WndProc is not usually a class member, the only way to do so is by declaring them with a global scope which is not always practical. In this post we will discuss a useful alternative to make the WndProc a method of the window class.

The trick is to use two functions: the actual WndProc to hanlde the window’s messages, and the other, a static one, to re-direct the original messages . Let’s consider this example class:


class WindowClass
{
static LRESULT CALLBACK MessageRouter(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
HWND windowHandle;

public:
WindowClass(int nCmdLn);
int Run();

};

For this strategy to work we need to perform some additional steps.

1. At the Window Class defintion, set MessageRouter as the lpfnWndProc pointer.

2. At the CreateWindow[Ex] function,  we set a pointer to the current instance (this) as the lParam parameter. We will recover this pointer at the MessageRouter function.


WNDCLASSEX w;
(...)
w.lpfnWndProc = MessageRouter;

(...)

windowHandle = CreateWindowEx(NULL, TEXT("MyClass"), TEXT("A window"),

WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL,

this); // <---- important!

Now let’s take a look at the MessaRouter function:


LRESULT CALLBACK WindowClass::MessageRouter(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
WindowClass* app;
if (msg == WM_CREATE)
{
app = (WindowClass*)(((LPCREATESTRUCT)lParam)->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)app);

}
else
{
app = (WindowClass*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
}
return app->WndProc(hWnd,msg,wParam,lParam);
}

If the window is being created (msg == WM_CREATE), we retrieve the pointer stored as a creation parameter, assign it to a pointer and savedit in the window’s extra data space using SetWindowLongPtr. Else, we recover the pointer from the window. Finally we call the actual WndProc function using the pointer (which is a member of the class and has access to all of its members and methods).

Even though there is an overhead of one extra function call when using this strategy, it’s small enough to be ignored.

You can download this example (source code and Visual Studio 2013 solution) where a counter (member of the class) is incremented when pressing a button and displayed when pressing the other one. This is all done in the WndProc function:


LRESULT CALLBACK WindowClass::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
switch (wParam)
{
case BUTTON_1:
this->count++;
MessageBeep(MB_ICONINFORMATION);
break;
case BUTTON_2:
MessageBox(hWnd,(wstring(L"You've pressed the button ")+ to_wstring(this->count)+wstring(L" times")).c_str(), TEXT("Information"), 0);
break;
}
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
CloseWindow(hWnd);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
break;
}
return 0;
}

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s