message_tap

Base class: HWND_vpapa

This class provides a WndProc that adds a this pointer and calls a virtual function on an object. It provides the mechanisms for subclassing an HWND.

See the Userís Guide for more discussion.


message_tap()

message_tap

constructor, public

The constructor is unsurprising.


~message_tap()

message_tap

destructor, virtual, public

The destructor will unhook the class, to ensure that subsequent messages donít get sent to a nonexistant object. The WinProc itself is a thunk thatís a member of this class, so the WinProc function pointer becomes invalid! Normally, the window owns this object so it wonít be destructed until after the window is destroyed, but usage can vary and the situation could be complex, so this is there just to be safe.


long call_old_wndproc (sMSG& msg)

message_tap

public

This calls the WndProc that the window had before it was subclassed by a call to hook, or calls DefWndProc if there wasnít one.

This calls the -W form of the Win32 CallWindowProc or DefWindowProc. This assumes that the current WndProc calling this is itself a Unicode form; that is, it is being sent Unicode-style messages. This is correct if called from handle_message, because hook installed it using the -W form of SetWindowLong.


WNDPROC_sig get_old_wndproc() const

message_tap

public

This returns the original WndProc that was displaced by hook.

See also: call_old_wndproc


WNDPROC_2 get_WndProc()

message_tap

public

This returns the generated callback function that is a thunk to turn a WNDPROC or WNDPROC_2 signature into a call to handle_message on this instance.

This is the value that hook will use as the WndProc to install. If you want to create a "window class" or dialog box using this function directly, then follow these guidelines to correctly attach this instance with the HWND.

You can call set_window_handle at first opportunity to complete the process, but the hook will automatically set it when it processes the first message, so it is guarenteed to be set by the time handle_message is called.

Note that you need to specify the WndProc when registering the "window class" (wndclass), and then all windows created will use the same WndProc. Unless the wndclass was designed for only a single window, this is not suitable for this class, since you must use a different instance of message_tap for each window created. So, you may want to call hook anyway, even in cases when you designed the wndclass, and use a generic (dummy) WndProc in the wndclass. That is, donít try and avoid calling hook just because you are not subclassing an existing window class.

For dialog boxes, on the other hand, you specify the DlgProc when you create the specific dialog box. This mechanism works well in this case. However, you do not want to call DefWindowProc, so should never call the base handle_message from your classís override! Recall that a Dialog Proc returns TRUE/FALSE (most of the time) to specify whether the message was handled. Your handle_message should do this when used as a Dialog Proc. So use Dialog_message_tap, which is designed specifically for this and takes care of these details and others.

It is an error to call this function after calling set_window_handle (including implicitly setting the window handle upon getting the first message). This catches errors from duplicate use of the same instance.


long handle_message (sMSG& msg)

message_tap

public, virtual

This function is called by the WndProc of the window to which it is attached. The sMSG structure (short MSG, since it is a subset (base class) of the MSG structure) is used to pass the hwnd, message, wParam, and lParam. Passing a structure rather than four parameters is better design, and passing by reference is more efficient than re-pushing all the parameters with all the forwarding going on.

The handling of WM_NCDESTROY, etc. as it relates to keeping up with this class is performed higher up in the plumbing before it calls this virtual function. So, you are not compelled to chain back to this base class implementation from your own classís override. Actually, you canít, due to technical limitations1 on the mix-in techniques. So the way you should end the derived classís implementation of this function is with return call_old_wndproc(msg); or return ratwin::window::DefWindowProc<wchar_t>(msg);

Likewise, this function does not deal with the pre-translation step. See hook_handler for a description of how these are handled invisibly to you.

Override this to do whatever you want, using the normal Win32 concepts of writing a WndProc. This is a Unicode WndProc, in that it should receive Unicode versions of messages. This is because the hook function used the -W form of SetWindowLong to install the thunk.

Overriding this virtual function in a derived class (or mix-in) is the main point of using the message_tap class.


void hook (HWND)

message_tap

public

This function associates this object with the specified Window. Call this after calling Win32 CreateWindow or otherwise obtaining an HWND to a valid Window.

This function will ďsubclassĒ the window, pointing the WndProc back to this instanceís handle_message function and remembering the old WndProc.

It is an error to call this more than once.

See also: set_window_handle, unhook


void hook_handler (MSG&)

message_tap

private

This function is the invisible plumbing that allows handle_message to not worry about internal handling and concentrate on the outward functionality.

This is where processing really begins, and it will decide whether to do something very special (unhook on destroy), call pre_translate_message, or call handle_message.

It also catches all exceptions, so they may propagate out of handle_message or pre_translate_message without problems, and calls the report_error function.


unsigned short LastMessage

protected, data

This contains the message code number that is seen as the ďlast messageĒ, and to unhook when it is processed. It is set at construction time to WM_NCDESTROY, but it may be changed if necessary.


void on_attach()

message_tap

public, virtual

This virtual function is called after the window and object are put together. It fills the role of WM_CREATE or WM_INITDIALOG, which canít be used because the call to hook is made after the window has been created.

Note that if you directly supply a message_tap as a WndProc, then on_attach is called much earlier than WM_CREATE, so your handle_message function will in this case receive WM_CREATE. But if a window is created, then hook is called, then handle_message will never get a WM_CREATE because it was issued long before this class became involved.

So, if this function is used to interact with the Window, not just complete its own data setup, it needs to be aware of when it might be invoked. For other uses, it needs only count on the fact that itís called at first oppertunity once the window handle and object are put together.


int pre_translate_message (const MSG& msg)

message_tap

public, virtual

This is called when the Tomahawk pre_translate_message message is processed. It basically does the same thing that you could do yourself by handling a WM_TOMAHAWK message from within your handle_message function, but itís such a common case, and needed for the Dialog_message_tap class, that itís already separated out for you.

This should be implemented by derived classes, for those windows that need some kind of message transformation done before dispatching. Specifically, the window that is a modeless dialog box, and any window that uses an Accelerator Table.

For regular windows, call the Win32 primitive TranslateAccelerator here. For modeless dialog boxes, call the Win32 primitive IsDialogMessage (that is what Dialog_message_tapís override does). For either, return 1 if the translate call returned true, or return 2 if the translate call returned false.

More generally, the return codes for this function are:

0
I have no clue! I might not even be aware that this mechanism exists. The caller will see this and keep looking elsewhere.
1
The message was processed, and should be discarded. Return this when Win32 TranslateAccelerator or Win32 IsDialogMessage produce true. The caller will see this value and know not to call Win32 DispatchMessage etc.
2
The message was processed and should not be discarded, or Iím sure it does not need to be processed. Return this when Win32 TranslateAccelerator or Win32 IsDialogMessage produce false. The caller will see this value and stop searching for something to pre-translate the message, and will continue with the other steps in the message pump (including Win32 DispatchMessage).

This base implementation always returns 0 and does nothing.

This is called by simple_message_pump::pre_translate.


void sane_check() const

message_tap

public

This uses a simple and cheap test to verify that the this pointer is indeed pointing to a non-destructed object of (or derived from) class message_tap.

This is used to trap Windows messages sent after the object is destructed, and to verify that untyped data (such as a generic lParam received in a message) correctly contains a pointer to an object as expected.

The test is not 100% reliable, as random memory could mimic the signature by coincedence, but is handy for spotting bugs and usage errors during development.


bool unhook (bool force=true)

message_tap

public

You are not expected to unhook a window explicitly, but to leave it hooked until the window is destroyed. You may call this function to unhook the window, but it will be called automatically in the destructor or when the HWND is closed.

To properly coordinate destroying the object, you must stop sending messages to it once the object is destroyed! So, part of the shutdown is to attempt to unhook the window proc. If nothing else had hooked the window after this class, then it restores the old pointer. Otherwise, (if force is true) it supplies a DefWindowProc, breaking the chain.

It returns true if it gracefully restored the window to its prehooked state, false if it had to be blunt and cut the chain (when force is true) or failed (when force is false).

This is automatically called when a WM_NCDESTROY message is handled, which is supposedly the last message processed. But who knows? Even if Microsoftís docs are correct, whatís to stop other code from sending messages directly? This unhooking hardens the code against this case. Also, derived classes can change this behavior by changing the value in LastMessage.

A future version might go to greater lengths to ensure it really is called last, even with other activity going on.

See also: unhook_when_possible, ~message_tap


void unhook_when_possible()

message_tap

public

Calling this function sets a flag, which instructs the class to unhook itself as soon as it is able, after all objects that subclassed the window after this object have restored their WndProc pointers.

This does not change the fact that the object will be forcably unhooked when WM_NCDESTROY is seen, as described under unhook.

See also: unhook, ~message_tap


Footnotes

Canít call message_tap::handle_message:

In order for the derived class to automatically hook up the sibling that uses the function (message_tap calls it) with the sibling that provides it (message_parliament defines it), only one sibling can define it. Even making it pure virtual in message_tap isnít enough—it cannot be defined in message_tap at all. Without this careful arrangement, the derived class would have to implement the function too, with a single line that calls the one in message_parliament. By defining it in one sibling only, the compiler realizes that it is providing the meaning for all the other siblings that only call it.