message_tap Userís Guide

Related Documents



This class provides a WinProc that adds a this pointer and calls a virtual function on a C++ object.

Many libraries provide some mechanism for associating a C++ object with a Win32 primitive HWND. When the WinProc is called for that window, it needs to find the associated C++ object and call the virtual function on it. Somehow, there needs to be a way to store that association and the plumbing code that goes in the “real” WinProc.

Windows provides a couple such mechanisms, including:

The first experimental version of Tomahawk went to some length to avoid the overhead of a Property, and so abstracted out the “message tap” with different concrete implementations for different situations. It could always use the Property if there wasnít a better way, but it could use a faster way in many common cases.

In this library, though, I use a totally different approach that is faster than any of the above. Instead of a WndProc that looks up the object (by whatever means) and calls a function on that object, I use a WndProc that is hard-coded with the address of the object and calls it directly without having to find anything.

This is possible by using dynamically-generated WndProcs, one per instance. That is the responsibility of the member_callback_thunk class. This message_tap class uses a member_callback_thunk, and encapsulates the mechanism for subclassing an HWND and all the related housekeeping issues.

Thatís all this class does: take care of the housekeeping of using the thunk as a WndProc. What it ends up calling is a virtual function called handle_message. It does not implement any kind of fancy message dispatching system or other things. It has amost no policy imposed, and can be used as a building-block for any kind of message handing system. For a more elaborate class that builds on this, see the eagduru class.

Threading Issues

Most of what this class does is associated with constructor/destructor activity, and as such does not expect functions to be called more than once, irreguardless of threads.

This class does not use any thread-specific data, so it does not matter which thread calls a particular member.

The object was not designed to be used by more that one thread simultainiously. So it is classified as OTT.

Housekeeping Issues

One Way road sign All instances of concrete types derived from message_tap must use new to create instances. The code within the class uses smart pointers and will delete this object when the reference count drops to zero. So, you must not create an instance on the stack or in static data.

Instances cannot be assigned or copied.

Instances use smart pointers to control their lifetime. As you would expect, the object is destroyed when the last smart pointer holding it is dropped. However, the Windows primitive HWND window also participates in the ownership of this object. Effectively (though the actual implementation is more circuitous), the window contains a smart pointer to this object. The interesting ramifications of this are:

How to Use

The program message_tap_demo.cxx contains a class testhook_1 which shows how to use the message_tap. The basic steps are:

  1. derive your class from message_tap.
  2. implement the handle_message member function.
  3. create the HWND window and instansiate your class.
  4. call hook.

Derive Your Class

The message_tap class is intended to be used as a base class. Instances of your concrete class are bound 1:1 to HWND windows. You supply a virtual function that is called as the WinProc of the window.

The handle_message Function

When Windows sends a message to the HWND window, it triggers the handle_message virtual function. Write this function in your concrete class to do whatever you want the window to do in response to various messages. This example uses the MSG classes to “crack” the windows messages. See that classís documentation for details. In this example, the program handles WM_MOUSEMOVE and WM_SIZE, just for demonstration purposes.

Create the HWND and the Instance

You create the HWND by whatever means (it is not handled by this class), and construct an instance of your concrete type. These are two distinct steps; they are performed separately.

ratwin::types::HWND hWnd= CreateWindow (WinclassName, "message_tap sample",  WS_OVERLAPPEDWINDOW|WS_VISIBLE);
testhook_1* p= new testhook_1;

In the InitInstance function of the sample program, the HWND is created using a call to the Win32 primitive CreateWindow. The instance is constructed using new, as it must.

The WinclassName used in the call to CreateWindow is discussed later.

The new testhook_1 is assigned to a simple pointer, not a smart pointer. That is fine since this code is not going to remember the value after InitInstance returns. The window itself keeps a reference to this object, and the object will be destructed when the window is closed. caution If I had kept a pointer (that is, not a smart pointer), it would point to an object that the smart pointer system might free, without itself participating in the lifetime control. In general, donít do that. But construction is a special case. Here, p is used only before the object is introduced to the smart pointer system, never used after the call that creates the first smart pointer to the object, and then goes out of scope. However, you could have written

classics::handle<testhook_1> h= new testhook_1;

if the situation were more complex.

Call hook

Once the two items—the HWND and the class instance—have been created, the final step is to put them together. This is done with the hook function inherited from the message_tap class.

p->hook (hWnd);  // now Window has (owns) a smart-pointer to it.

Once hook is called, your object starts receiving messages from that window.

Note that InitInstance returns and neither hWnd nor p is remembered anywhere other than in the local variables that just went out of scope. There is no need to keep track of them within the program, since the window keeps track of the object and the window is on the screen.

About the WNDCLASS

As explained in the Overview, this window-hooking mechanism does not use a single WinProc shared among all windows that use the system; rather, every window has a unique run-time-generated WinProc. So, what WinProc should be used when creating the window?

Since you are going to subclass the window anyway, it really doesnít matter what WinProc is used to create it. Generally, the default DefWinProc in Windows will do just fine. But, if there is any message handling going on in the original WinProc, it will still be called when your handle_message decides not to handle a message and calls the base-class handler.

The actual WinProc used in this program is really_minimal_window_proc found in RatWin. The only thing is handles is to quit the program when the window is closed; otherwise it just calls DefWinProc. This is good for a main window, as itís one less thing to worry about in the program.

Everything is taken from the default constructor of window_class, which is a RatWin class around the WNDCLASS.

A Building Block

Low-Level Compatible

Special Messages

... broken out into their own callbacks, or handled internally

Error Trapping

Besides handing a few important messages, this class will also prevent the Windows callback from returning abnormally. ...

More About the Example Code

see dialog, message pump

Advanced Usage and Tips

Self-Contained WNDCLASS

To make a self-contained wndclass, make a WinProc that constructs the instance.