Class Members as Callbacks

This is a topic that pops up regularly on
Microsoft’s Visual C++ newsgroups. In these
days, many people are changing, evolving from procedural designs to more object-oriented
ones. Under Windows, where callbacks are a necessary evil, this causes some trouble.

It’s necessary to understand why the trouble arises in the first place.
As many of you know, callbacks are a special kind of function you don’t call yourself directly.
Instead, you give a pointer to the function to another function, so that Windows can call
it when it’s need it. An example of this are windows procedures, and enumeration functions.

To make this process work correctly, strict typedefs are define in the
Windows header files. If you try to use a class member as a callback, the compiler will
give you an error, saying that the member prototype does not match the required typedef.

You know you defined the member correctly, yet it doesn’t work. So you try using the
address of operator ‘&’, and all you get is a different error from the compiler. What’s up?
The answer is simple, yet is not clearly stated anywhere. In C++, class member functions
have a hidden parameter on the stack, which is actually the this
pointer. That’s what allows a member function to access other members in the particular
class instance.

There’s only one way to get rid of the this pointer: making the member
static. This brings other, more important and hard to solve problems. If you can’t access
the class from your callback, then what’s the use of putting it in a class? You have to
find a way of getting around this limitation. The most ugly hack is, of course, putting the
instance of the class as a global variable, which the callback can access. While this solves the
problem, it isn’t very object oriented, and it leads to code that can be hard to maintain.

Windows itself gives you the answer to this problem for many of the
most used callbacks. Many functions that require callbacks allow you to pass an extra, application
defined, parameter to the callback, which you can use to give the callback access to the class instance. An example of
this are the CreateWindow(), EnumChildWindows(), EnumResourceNames()
and many others.

The above mentioned solutions are the easiest ways to get around this
problem, but are not the only ones. If you’re good in assembly (which I’m not, so I’m not even going
to try and explain it), you can write a little stub that allows you to use a non static member function as a callback.
For this you need to write a piece of inline assembly in a static member of the class, and use it
as callback. This stub then handles parameters to the *real* non static member that does the
real work. This is very similar to what Microsoft’s own Active Template Library (ATL) does
for it’s window procedure implementation.

A particular case of this problem is creating window classes.
Well, the easiest way is to use the mechanism provided by the Window creation
routines themselves. What you can do, is have your window class have a static
WndProc method, that you register as the window’s window procedure. To give the
this pointer to WndProc, you have to take a two step approach:

  1. Pass the this pointer in the LPARAM argument of
    CreateWindow()

  2. On WndProc, when you handle WM_CREATE, store the pointer
    received on the window data, using SetWindowLong() with GWL_USERDATA
    index.

On Subsequent messages, you can then retrieve the instance
pointer through GetWindowLong(), cast it back to your class and call through it.
One note though: If your class is a dialog wrapper, you can’t use GWL_USERDATA
to store the pointer. Instead, you need to use the DWL_USER index.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>