WNDCLASS
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:
WNDCLASS. This is the most straightforward, but
is only applicable when you designed the WNDCLASS for this purpose. It cannot work
on subclassed windows or controls.
GWL_USERDATA. But only if no other code is already using it.
DWL_USER data or other data area as provided by various WNDCLASS’s. But
each WNDCLASS has 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 HWND is closed.
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.
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
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:
The program message_tap_demo.cxx contains a class testhook_1
which shows how to use the message_tap. The basic steps are:
message_tap.
handle_message member function.
HWND window and instansiate your class.
hook.
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.
handle_message FunctionWhen 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.
HWND and the InstanceYou 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.
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.
hookOnce 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.
WNDCLASSAs 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.
... 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.