출처: http://www.edm2.com/0201/hooks.html
Utilizing Hooks for Added CapabilitiesWritten by Larry Salomon, Jr. |
IntroductionBeginning with the MVS operating system, user exits have existed in order to give those with a bit of programming skill the opportunity to change the default behavior for a particular event. Since then, these have evolved into system hooks, which provide the same ability; unfortunately, whether known as user exits or system hooks, there has been a lack of documentation on how to utilize these provisions from with OS/2 applications. This article will discuss what hooks are, what the different types of hooks are, and the uses of some of the various types of hooks. Finally, we will look at a (new) version of Life Saver, which uses one of the hooks to implement a screen saver.
Going Fishing?What is a hook? In the sport of fishing, a hook attaches the fishing line) to the fish so that the line follows the movement of the fish. In programming, the concept is similar - you use a hook to attach your application to an event so that your application knows whenever the event occurs. Why is this useful? Consider an application that needs to know when the system is rebooting, or needs to know whenever the PrintScreen key was pressed (regardless of who has the focus). These types of things can only be done with hooks. OS/2 defines 17 types of hooks that can be set.
Two other hooks - HK_PLIST_ENTRY and HK_PLIST_EXIT - are also defined in pmwin.h but they do not exist within the system (as far as I know).
Hook ContextsSome hooks can be defined as application-wide or system-wide; the difference is that an application-wide hook will be called whenever the event occurs within the application, while a system-wide hook will be called whenever the event occurs anywhere within the system. Because of this property of system-wide hooks, the corresponding hook procedure must reside within a DLL. This has other interesting connotations which we will see later. Some hooks must be application-wide, while others must be system-wide. Such hooks do not have a choice.
Chaining HooksIn reality, a hook is nothing more than a function that accepts one or more arguments. The function is called by the system, meaning that the function must be exportable and use the _System calling convention. Additionally, the system must be made aware of the hook's existance; it is registered via the function WinSetHook(). Similarly, when the hook is to be removed, the function WinReleaseHook() is called. BOOL WinSetHook(HAB habAnchor, HMQ hmqQueue, LONG lHookType, PFN pfnHook, HMODULE hmModule); BOOL WinReleaseHook(HAB habAnchor, HMQ hmqQueue, LONG lHookType, PFN pfnHook, HMODULE hmModule);Both function take the following parameters:
This is important because some Win functions install hooks themselves, such as the WinCreateHelpInstance() function. Since the help hook installed by this function indicates to the system that no other hooks after it should be called, any help hooks you install before calling this function will never see any help events.
Two Examples
A Small ExampleConsider the application provided as hooks.zip. It is nothing more than a dialog box with two hooks attached - the associate window HDC hook, and the destroy window hook. Whenever either hook is invoked, they beep if the window provided is a frame window. BOOL EXPENTRY assocWindowHook(HAB habAnchor, HDC hdcContext, HWND hwndAssoc, BOOL bAssoc) //------------------------------------------------------------------------- // This hook is invoked whenever an HDC is associated or disassociated with // a window. // // Input: habAnchor - anchor block of the thread in whose context the // event occurred. // hdcContext - handle of the device context. // hwndAssoc - handle of the window associated/disassociated. // bAssoc - TRUE if hwndAssoc is associated with hdcContext. FALSE // otherwise. // Returns: TRUE if successful processing, FALSE otherwise. //------------------------------------------------------------------------- { CHAR achClass[256]; WinQueryClassName(hwndAssoc,sizeof(achClass),achClass); if ((WinFindAtom(WinQuerySystemAtomTable(), achClass)==LOUSHORT(WC_FRAME)) && bAssoc) { WinAlarm(HWND_DESKTOP,WA_NOTE); } /* endif */ return TRUE; } BOOL EXPENTRY destroyWindowHook(HAB habAnchor, HWND hwndDestroy, ULONG ulReserved) //------------------------------------------------------------------------- // This hook is invoked whenever a window is destroyed. // // Input: habAnchor - anchor block of the thread in whose context the // event occurred. // hwndDestroy - handle of the window being destroyed. // ulReserved - reserved. // Returns: TRUE if successful processing, FALSE otherwise. //------------------------------------------------------------------------- { CHAR achClass[256]; WinQueryClassName(hwndDestroy,sizeof(achClass),achClass); if (WinFindAtom(WinQuerySystemAtomTable(),achClass)==LOUSHORT(WC_FRAME)) { WinAlarm(HWND_DESKTOP,WA_ERROR); } /* endif */ return TRUE; }Figure 3) Two simple hook procedures. The hooks are installed, as we noted, using the WinSetHook() call and are released using the WinReleaseHook() call. WinSetHook(habAnchor, HMQ_CURRENT, HK_WINDOWDC, (PFN)assocWindowHook, NULLHANDLE); WinSetHook(habAnchor, HMQ_CURRENT, HK_DESTROYWINDOW, (PFN)destroyWindowHook, NULLHANDLE); : : WinReleaseHook(habAnchor, HMQ_CURRENT, HK_WINDOWDC, (PFN)assocWindowHook, NULLHANDLE); WinReleaseHook(habAnchor, HMQ_CURRENT, HK_DESTROYWINDOW, (PFN)destroyWindowHook, NULLHANDLE);Figure 4) WinSetHook() and WinReleaseHook() calls. Note that the hooks are local to the message queue for the thread. Also, note how the hook procedures are cast to the generic type PFN to avoid warnings/errors by the compiler.
A Large ExampleNow, look at the application provided in life.zip. This is a screen saver which utilizes the input hook to determine when a period of inactivity elapses; actually, the word should be "expires" since the input hook is invoked when there is no inactivity. The strategy for the application is to start a timer using WinStartTimer() to count the number of seconds of inactivity. Whenever the input hook is invoked, it tells the application to reset this counter. Should the threshold be reached, the screen saver goes into effect. There are a couple of interesting points to be made:
SummaryHooks provide a way to code certain types of logic into your application, which could not be done (easily) in other ways. While they are very useful, care must be taken because, in the case of system-wide hooks, what you do affects the entire system. For example, in a send message hook, you wouldn't use WinSendMsg() to notify your application, or else you would have an endless loop. For all development involving hooks, one must be sure that the design is completely worked out, or you could spend fruitless hours debugging your hook...and these are not easy beasts to debug, either. Probably the worst detriment during development is the lack of good documentation. Many of the hooks are scantily documented in pmwin.h at best, while others have incorrect parameters listed, making things even more difficult. If you have access to CompuServe, the best help you can get is from the IBMers that reside there, since they have been using many of these hooks since their first appearance. |