With PM (and Windows too), windows communicate with messages. The system sends messages to windows automatically for all kinds of events, and windows can send messages to each other as well. There are hundreds of different system messages, and application can define their own messages too.
For example, if you click on any window with the left mouse button, that
window is sent the the WM_BUTTON1CLICK
message by the system.
What happens then
depends on the type (in PM: the class) of the window. For example, a container
control will look if there's a container record under the mouse pointer and, if so,
select the corresponding record. A Netscape client window will see if there's
a link under the mouse pointer and, if so, load a new HTML page to follow the link.
There are many more "important" system messages, such as WM_CHAR
(for keystrokes), WM_SIZE
(when windows are resized),
WM_PAINT
(when windows need repainting), and
WM_DESTROY
(when windows are destroyed). That's basically why PM
programming seems so complicated at first, because PM programming mostly means
reacting to messages, which is quite different from "regular" programming,
where programs are normally simply processing tasks sequentially.
With PM, if no messages are queued for a window, a program normally simply does
nothing. Its threads are blocked until messages come in.
Now, when a global message hook function is registered with the system, all messages go through that hook function before the target window receives that message. This way a hook function can choose to modify the message, redirect it to a different window, do something completely different itself, or even swallow the message so that the target window won't receive it.
This is how all the well-known PM hook utilities (such NPS WPS, FeelX, ProgramCommander/2, X-it, Styler/2, and the &xwp; PM hook as well) work. They intercept certain messages and manipulate them.
For example, every time the &xwp; PM hook encounters a WM_CHAR
message (that is, when a key on the keyboard is pressed or released), it will check
if that key is on the global hotkeys list. If so, the message is swallowed (so
that the target window won't receive it) and
some part of the &xwp; WPS code is notified instead, which can then open
the corresponding object which has been defined by the user for that hotkey.
The way the &xwp; hook is implemented differs slightly from that of the typical hook utilities. Most of those utilities register their hook function with PM when they are started and de-register the hook again when the program is ended (for example, if "close" is chosen from the window list).
Instead, &xwp;'s PM hook is registered from a special program which is not visible
in the window list -- the &xwp; daemon, XWPDAEMN.EXE
. You can
see it with WatchCat or any other process lister though. Try killing that program:
you will see that all hook functionality will be gone.
XWPDAEMN.EXE
is started by &xwp; while the WPS is starting up
for the first time and keeps running until the system is shut down. That is, the
daemon even keeps running between Desktop restarts.
Even though the daemon is a PM program, it doesn't create any windows (except for the &pgr; window, see below).
All this is pretty complex in detail because this involves using shared memory and proper serialization techniques such as shared mutex semaphores and cross-process messaging, especially since the hook is configured from WPS settings pages, which are running in the WPS process. But I hope you get the basic idea.
There are a number of good reasons why I chose that approach:
PMSHELL.EXE
process, which all WPS classes are running in. If the WPS
crashes, it's more difficult to clean up installed hooks because the WPS is so
complex in itself.
For details about &pgr;, see "How it works" in the &pgr; section.