Delegators #2 – C++ & Win32

Last post I was talking about using SetProp/GetProp to link between an HWND and an instance to a class that encapsulates the window widget. There’s another way to do it.

The first time you are taught how to create a window in Win32, they tell you that you have to fill in all fields in the WNDCLASS structure, but they always keep on saying to ignore some of the advanced fields. One of the fields inside that structure is cbWndExtra, this one says to the windows manager how many bytes to allocate after the original windows manager’s structure. So suppose all we need is to save the ‘this’ pointer, it means we will need 4 bytes, in 32bits environment system, of course. So we can set cbWndExtra to 4, and then we have a DWORD we can access to as much as we like with the SetWindowLong/GetWindowLong API.

Something like this would suffice:

WNDCLASS wc;
wc. fields = …
wc.lpszClassName = “myclass”;
wc.cbWndExtra = 4;
RegisterClass(&wc);
HWND hWnd = CreateWindow(“myclass”, …)
SetWindowLong(hWnd, 0, this);
ShowWindow(…);

..

And later on in the window-procedure, you can do this:

LRESULT WINAPI MyWindow::WndProc(HWND hWnd, UINT message, WPARAM wparam, LPARAM lparam)
{
 MyWindow* pThis = (MyWindow*)GetWindowLong(hWnd, 0);
 if (pThis != 0) return pThis->WndProc(message, wparam, lparam);
 return DefWindowProc(hWnd, message, wparam, lparam);
}

 And there you go. There are a few problems with this technique though. You can’t handle the WM_CREATE message inside the instance’s WndProc, that’s because before CreateWindow returns, the global WndProc will get called with this message, and you didn’t have the chance to set the ‘this’ pointer…

The second problem is that this technique is good only for windows you create yourself, because you control the WNDCLASS… You can use pre-created window-classes of the system by changing their fields in your application only, I think this will create a copy of the pre-created classes only for your process.

Anyhow, to solve the first problem, I saw implementation that create the instance of the object when they receive the WM_CREATE inside the global WndProc. This is really a matter of design, but this way you can call an initializer of the instance once WM_CREATE was received. I can’t come up with another way at the moment if the instance is already created. Maybe just call the initializer on the first message the window gets? Well it’s a bit cracky.

3 Responses to “Delegators #2 – C++ & Win32”

  1. lorg says:

    Although specifically for this case it won’t work, there are ways to work out similar problems. (If the WndProc wasn’t shared across different instances).

    Here is some example code:

    void * my_callback(void* arg) {
    //bla
    }
    typedef void* (*callback_func)(void *);
    void some_caller(callback_func f);

    You want my_callback to also get another argument, not passed along by some_caller. Well, in Python you’d do either of the following – pass a functor, an object with a __call__ method, or use functools.partial, or any other equivalent trick.
    Turns out, in C++ you could do the same thing, using boost.bind (and other similar libraries), and also, create a functor (an object with operator()).

    Besides that, nice one!

  2. arkon says:

    Oh I will take a look at boost.bind. In Python this all thing is a joke. That’s why some libraries use Assembly. I will cover that in next ‘delegators’ post.

  3. lorg says:

    By the way, Boost has libraries other than bind that achieve similar purposes. Check out http://www.boost.org/libs/libraries.htm#Function-objects

Leave a Reply