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
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:
WNDCLASS. This is the most straightforward, but is only applicable when you designed the
WNDCLASSfor this purpose. It cannot work on subclassed windows or controls.
GWL_USERDATA. But only if no other code is already using it.
DWL_USERdata or other data area as provided by various
WNDCLASSís. But each
WNDCLASShas its own way of doing it, so this is not general purpose.
HWND! This is not a problem with the Map idea, but a limitation in their implementation. A general implementation would essentially duplicate the built-in Property mechanism, and is complicated by the need to remove the entry when the
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
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.
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.
All instances of concrete types derived from
message_tap must use
new to create instances. The code within the class uses smart pointers
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:
message_tap_demo.cxx contains a class
which shows how to use the
message_tap. The basic steps are:
HWNDwindow and instansiate your class.
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.
When Windows sends a message to the
HWND window, it triggers the
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_SIZE, just for demonstration
HWNDand 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.
WinclassName used in the call to
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.
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.
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
p->hook (hWnd); // now Window has (owns) a smart-pointer to it.
hook is called, your object starts receiving messages from that window.
InitInstance returns and neither
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
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
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
... broken out into their own callbacks, or handled internally
Besides handing a few important messages, this class will also prevent the Windows callback from returning abnormally. ...
see dialog, message pump
To make a self-contained wndclass, make a WinProc that constructs the instance.