In general, a Presentation Manager (PM) hook is a piece of code which is registered with PM to receive all messages which are going through the system. As a result, that piece of code effectively becomes part of every process on the system which is processing messages.

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:

  1. It's usually not a good idea to start hooks from the 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.

  2. Since the daemon keeps running between Desktop restarts, it can also keep track of certain data which is needed between Desktop restarts. For example, the &xwp; startup folder shouldn't be processed again when the WPS is restarted (unless the user requests this). Try it out: If you kill the daemon and then restart the WPS, you'll see that the &xwp; startup folder will get processed again, because the &xwp; WPS classes see that the daemon is not running and then think that the WPS is starting up for the first time.

  3. &pgr; is part of the daemon as well and is thus independent of the WPS. &pgr; requires lots of message monitoring as well since it needs to know about window creation and destruction. At any point in time, &pgr; knows about every single window on the system. It's much easier for &pgr; to keep its own windows apart from the rest of the system when it's running in a separate process.

    For details about &pgr;, see "How it works" in the &pgr; section.