PMGuide - Hooks
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation
A hook is a point in a system-defined function where an application can supply additional code that the system processes as though it were part of the function. This chapter describes how to use hooks in PM applications.
About Hooks
Many operating system functions provide points where an application can hook in its own code to enhance or override the default processing of the function. Most hooks enable an application to monitor some aspect of the message stream. For example, the input hook enables an application to monitor all messages posted to a particular message queue.
A hook function can be associated with the system-message queue, so that it monitors messages for all applications. These system-queue hook functions can be called in the context of any application. However, they must be defined in separate dynamic link library (DLL) modules, because it is not possible to call application-module procedures from other applications.
A hook function can also be associated with the message queue of an individual thread, so that it monitors messages for that thread only. These message-queue hook functions are called only in the context of the thread. Therefore, these hook functions are typically defined locally.
OS/2 operating system contains many types of hooks, and the system maintains a separate hook list for each type of hook supported.
Hook Lists
A hook list contains the addresses of the functions that the system calls while processing a hook. An application can take advantage of a particular type of hook by defining a hook function and using WinSetHook to enter the address of the function in the corresponding hook list. To specify the hook type in WinSetHook, the application uses one of the following constants:
Constant Name | Description |
---|---|
HK_CHECKMSGFILTER | Lets applications apply very specific message filtering. See HK_CHECKMSGFILTER - Check Message Filter Hook. |
HK_CODEPAGECHANGED | Lets applications determine when the code page changes. See HK_CODEPAGECHANGE - Code Page Changed Hook. |
HK_DESTROYWINDOW | Called whenever a window is destroyed. See HK_DESTROYWINDOW - Destroy Window Hook. |
HK_FINDWORD | Lets applications control where WinDrawText places line breaks. See HK_FINDWORD - Find Word Hook. |
HK_FLUSHBUF | Lets applications save data before the system reboots. See HK_FLUSHBUF - Flush Buffer Hook. |
HK_HELP | Monitors the WM_HELP message. See HK_HELP - Help Hook. |
HK_INPUT | Monitors messages in the specified message queue. See HK_INPUT - Input Hook. |
HK_JOURNALPLAYBACK | Lets applications insert messages into the system message queue. See HK_JOURNALPLAYBACK - Journal Playback Hook. |
HK_JOURNALRECORD | Lets applications record mouse and keyboard input messages. See HK_JOURNALRECORD - Journal Record Hook. |
HK_LOADER | Lets the library and procedure loading and deleting calls be intercepted. See HK_LOADER - Loader Hook. |
HK_LOCKUP | Called when the system locks itself up. See HK_LOCKUP - Lockup Hook. |
HK_MSGCONTROL | Monitors the flow of messages to be intercepted. See HK_MSGCONTROL - Message Control Hook. |
HK_MSGFILTER | Monitors input events during system modal loops. See HK_MSGFILTER - Message Filter Hook. |
HK_MSGINPUT | Lets applications simulate user input, and only mouse and keyboard messages should be passed in. All other messages will be discarded. Mouse and keyboard messages injected into this hook will have the same effect as if they were generated by the mouse or keyboard device driver. The messages are routed in the same manner as normal user input. See HK_MSGINPUT - Message Input Hook. |
HK_PLIST_ENTRY | Called every time a program-list call or initialization file call is invoked by an application. It is called before the call is run. See HK_PLIST_ENTRY - Program List Call Hook. |
HK_PLIST_EXIT | Called every time a program-list call or initialization file call is invoked by an application. It is called before the call is run. See HK_PLIST_EXIT - Program List Exit Hook. |
HK_REGISTERUSERMSG | Called whenever a user message or data type is registered. See HK_REGISTERUSERMSG - Register User Message Hook. |
HK_SENDMSG | Monitors messages sent by using WinSendMsg. See HK_SENDMSG - Send Message Hook. |
HK_WINDOWDC | Called when a device context is allocated or freed. See HK_WINDOWDC - Device Context Hook. |
While running a function that contains a hook, the system checks for any function addresses in the hook list that correspond to the type of hook. If an address is found, the system tries to locate and run the function.
Hook Chains
In the hook lists associated with most message-monitoring hooks, the function addresses are linked to form chains. The system passes a message to each hook function in the list, one after the other. Each function can modify the message or stop its progress through the chain, thereby preventing it from reaching the next hook or the destination window. The system calls chained hook functions in last-installed, first-called order.
Hook Types
Each type of hook passes a characteristic set of arguments to the functions referenced in the corresponding hook list. For an application to use a particular hook, it must define a function that processes those arguments and enter the address of the function in the hook list using WinSetHook. This section describes the types of hooks available in OS/2 operating system and the requirements of the functions that process each hook type.
HK_CHECKMSGFILTER - Check Message Filter Hook
The check message filter hook is called whenever WinGetMsg, WinWaitMsg, or WinPeekMsg are used to filter message identities. This hook lets an application apply very specific message filtering, for example, based on the values of message parameters. This hook is called after window handle filtering and before message filtering. The following code shows the syntax for a check message filter hook function:
BOOL EXPENTRY CheckMsgFilterHook ( HAB hab, PQMSG pQmsg, ULONG usFirst, ULONG usLast, ULONG fOptions);
The hab parameter is the anchor block handle. The pQmsg parameter is a pointer to a QMSG data structure that contains information about the message. The usFirst parameter is the first message identity specified on a call to the WinGetMsg, WinPeekMsg, or WinWaitMsg function. The usLast parameter is the last message identity specified on a call to the WinGetMsg, WinPeekMsg, or WinWaitMsg function. The fOptions parameter indicates whether or not the message is removed from the queue:
- PM_NOREMOVE
- PM_REMOVE
If the check message filter hook function returns TRUE, the message is accepted by the filtering. Any further check message filter hooks in the chain are ignored, any filtering specified by the WinGetMsg, WinPeekMsg, and WinWaitMsg functions are ignored, and processing of the message continues. A hook that always returns TRUE effectively switches off message filtering. If the check message filter hook function returns FALSE, the message is passed on to the next check message filter hook in the chain. If the end of the chain has been reached, the filtering specified by the WinGetMsg, WinPeekMsg, or WinWaitMsg functions is applied.
HK_CODEPAGECHANGE - Code Page Changed Hook
The code page changed hook notifies an application when the code page associated with the specified message queue has been changed. The system calls a code page changed hook function after setting the new code page. Typically, the code page changed hook is used in applications that support multiple languages. The following code shows the syntax for a code page changed hook function:
VOID EXPENTRY CodePageChangedHook(HMQ hmq, USHORT usOldCodepage, USHORT usNewCodepage);
The hmq parameter receives the handle of the message queue that is changing its code page. The usOldCodepage is the code page identifier of the previous code page. The usNewCodepage parameter is the identifier of the new code page. A code page changed hook function does not return a value, and the system always calls the next function in the chain.
HK_DESTROYWINDOW - Destroy Window Hook
The destroy window hook is called whenever a window is destroyed. The following code shows the syntax for a destroy window hook function:
BOOL EXPENTRY DestroyWindowHook (HAB hab, HWND hwnd, ULONG ulReserved);
This hook is sent after the WM_DESTROY message has been sent and just before the window becomes invalid. The hab parameter is the anchor block handle. The hwnd parameter is the handle of the window being destroyed. The ulReserved parameter is reserved. When this hook function returns TRUE, the function completed successfully. When it returns FALSE, an error occurred.
HK_FINDWORD - Find Word Hook
The find word hook allows an application to control where WinDrawText breaks a character string that is too wide for the drawing rectangle. If the DT_WORDBREAK flag is set, the system calls this hook from within WinDrawText. Typically, this hook is used to avoid awkward line breaks in applications that use double-byte character sets. The following code shows the syntax for a find word hook function:
BOOL EXPENTRY FindWordHook(USHORT usCodepage, PSZ pszText, ULONG cb, ULONG ich, PULONG pichStart, PULONG pichEnd, PULONG pichNext);
The usCodePage parameter contains the code page identifier of the string to be formatted; the pszText parameter contains a pointer to the actual string. The cb parameter contains a value specifying the number of bytes in the string. This value is 0 if the string is null-terminated. The ich parameter contains the index of the character in the string that intersects the right edge of the drawing rectangle. A find word hook function uses these four parameters to determine the word that contains the intersecting character. It then fills the remaining three parameters, pichStart, pichEnd, and pichNext, with the indexes of the starting character of the word, ending character of the word, and starting character of the next word in the string. If the find word hook function returns TRUE, WinDrawText draws the string only up to, but not including, the specified word. If the function returns FALSE, WinDrawText formats the string in the default manner.
HK_FLUSHBUF - Flush Buffer Hook
The flush buffer hook allows applications to save data before the system reboots. The following code shows the syntax for a flush buffer hook function:
BOOL EXPENTRY FlushBufHook (HAB hab);
This hook is called to notify applications that the system is rebooting due to a Ctrl+Alt+Del sequence being entered. It enables applications to write existing information to the hardfile immediately, avoiding loss of data before the system reboots. Template:Note The hab parameter is the application anchor block. When this function returns TRUE, the function completed successfully. When this function returns FALSE, an error occurred.
HK_HELP - Help Hook
The help hook allows an application to include online help. The system calls a help hook function during the default processing of the WM_HELP message. Help processing is done in two stages: creating the WM_HELP message and calling the help hook. The WM_HELP message can come from the following sources:
- WM_CHAR message, after translation by an ACCEL data structure with the AF_HELP style. The default system accelerator table translates the F1 key into a help message. The WM_HELP message is posted to the current focus window, which can be a menu, a button, a frame, or your client window.
- Menu-bar selection, when the MIS_HELP style is specified for the menu-bar item. The WM_HELP message is posted to the current focus window.
- Dialog-window push button, when the BS_HELP style is specified for the push button. The WM_HELP message is posted to the owner window of the button, which normally is the dialog window.
- Message box, when the MB_HELP style is specified for the message box. The WM_HELP message is posted to the message box.
The WM_HELP message is posted to the current focus window. The default processing in WinDefWindowProc is to pass the message up to the parent window. If the message reaches the client window, it can be processed there. If the message reaches a frame window, the default frame-window procedure calls the help hook. The help hook is also called if a WM_HELP message is generated while the application is in menu mode, that is, while a selection is being made from a menu. The following code shows the syntax for a help hook function:
BOOL EXPENTRY HelpHook(HAB hab, ULONG usMode, ULONG idTopic, ULONG idSubTopic, PRECTL prcPosition)
If a help hook function returns TRUE, the system does not call the next help hook function in the chain. If the function returns FALSE, the system calls the next help hook function in the chain. The arguments passed to the function provide contextual information, such as the screen coordinates of the focus window and whether the message originated in a message box or a menu. The WM_HELP message often goes to a frame window instead of to the client window. The frame window processes a WM_HELP message as follows:
- If the window with the focus is the FID_CLIENT window, the frame window passes the WM_HELP message to the FID_CLIENT window.
- If the parent of the window with the focus is the FID_CLIENT frame-control window, the frame window calls the help hook, specifying the following:
Mode = HLPM_FRAME Topic = frame-window identifier Subtopic = focus-window identifier Position = screen coordinates of focus window
- If the parent of the focus window is not an FID_CLIENT window (it could be the frame window or a second-level dialog window), the frame window calls the help hook, specifying the following:
Mode = HLPM_WINDOW Topic = identifier of parent of focus window Subtopic = focus-window identifier Position = screen coordinates of focus window
An application receives the WM_HELP message in its dialog-window procedure. The application can ignore the message, in which case the frame-window action occurs as described, or the application can handle the WM_HELP message directly. Menu windows receive a WM_HELP message when the user presses the Help accelerator key (F1 by default) while a menu is displayed. Menu windows process WM_HELP messages by calling the help hook, specifying the following:
Mode = HLPM_MENU Topic = identifier of pull-down menu Subtopic = identifier of selected item in pull-down menu Position = screen coordinates of selected item
A help hook function should respond by displaying information about the selected menu item. WinDefWindowProc processes WM_HELP messages by passing the message to the parent window. Typically, the message moves up the parent chain until it arrives at a frame window.
HK_INPUT - Input Hook
The input hook enables an application to monitor the system-message queue or an application-message queue. The system calls an input-hook function whenever WinGetMsg or WinPeekMsg is about to return a message. Typically, an application uses the input hook to monitor mouse and keyboard input and other messages posted to a queue. The following code shows the syntax for an input-hook function:
BOOL EXPENTRY InputHook(HAB hab, PQMSG pQmsg, ULONG fs)
The pQmsg parameter is a pointer to a QMSG data structure that contains information about the message. The fs parameter of InputHook can contain the following flags from WinPeekMsg, indicating whether or not the message is removed from the queue:
PM_NOREMOVE PM_REMOVE
If an input-hook function returns TRUE, the system does not pass the message to the rest of the hook chain or to the application. If the function returns FALSE, the system passes the message to the next hook in the chain or to the application if no other hooks exist. An input-hook function can modify a message by changing the contents of the QMSG data structure, then returning FALSE to pass the modified message to the rest of the chain. The following problems can occur when a hook modifies a message:
If the caller uses WinPeekMsg or WinGetMsg with a message filter range (msgFilterFirst through msgFilterLast), the message is checked before the hook functions are called, not after. If the input-hook function modifies the msg field of the QMSG data structure, the caller can receive messages that are not in the range of the message filter of the caller. If the input-hook function changes a WM_CHAR message from one character into another—for example, if the function modifies all Tab messages into F6 messages—an application that depends on the key state is unable to interpret the result. (When the Tab key is translated into the F6 key, the application receives the F6 keystroke and enters a process loop, waiting for the F6 key to be released; the application calls WinGetKeyState with the HWND_DESKTOP and VK_F6 arguments).
HK_JOURNALPLAYBACK - Journal Playback Hook
The journal playback hook enables an application to insert messages into the system-message queue. Typically, an application uses this hook to play back a series of mouse and keyboard events that were recorded earlier using the journal record hook. A journal playback hook function can be associated only with the system-message queue. Regular mouse and keyboard input is disabled as long as a journal playback hook is installed. It is important to notice that, because mouse and keyboard input are disabled, this hook can easily hang the system. The following code shows the syntax for a journal playback hook function:
ULONG EXPENTRY JournalPlaybackHook(HAB hab, BOOL fSkip, PQMSG pQmsg)
The pQmsg parameter is a pointer to a QMSG data structure that the journal playback hook function fills in with the message to be played back. If the fSkip parameter is FALSE, the function fills in the QMSG data structure with the current recorded message. The function returns the same message each time it is called, until fSkip is TRUE. The same message is returned many times if an application is examining the queue but not removing the message. If fSkip is TRUE, the function advances to the next message without filling in the QMSG data structure, because the pQmsg parameter is NULL when fSkip is TRUE. The journal playback hook returns a ULONG time-out value that tells the system how many milliseconds to wait before processing the current message from the playback hook. This enables the hook to control the timing of the events it plays back. The time field of the QMSG data structure is filled in with the current time before the playback hook is called. The hook should use the time stored in this field, instead of the system clock, to set up delays between events.
HK_JOURNALRECORD - Journal Record Hook
The journal record hook allows an application to monitor the system-message queue and to record input events. Typically, an application uses this hook to record a sequence of mouse and keyboard events that it can play back later by using the journal playback hook. A journal record hook function can be associated only with the system-message queue. The following code shows the syntax for a journal record hook function:
VOID EXPENTRY JournalRecordHook(HAB hab, PQMSG pQmsg)
The pQmsg parameter is a pointer to a QMSG data structure containing information about the message. The system calls the journal record hook function after processing the raw input enough to create valid WM_CHAR or mouse messages and after setting the window-handle field of the QMSG data structure. A journal record hook function does not return a value, and the system always calls the next function in the chain. Typically, a journal record hook function saves the input events to a disk file to be played back later. The hwnd field of the QMSG data structure is not important and is ignored when the message is played back. The following messages are passed to the journal record hook:
WM_CHAR WM_BUTTON1DOWN WM_BUTTON1UP WM_BUTTON2DOWN WM_BUTTON2UP WM_BUTTON3DOWN WM_BUTTON3UP WM_MOUSEMOVE.
The positions stored in the mouse messages are in screen coordinates. The system does not combine mouse clicks into double clicks before calling the hook, because there is no guarantee that both clicks will be in the same window when they are played back. The system passes a WM_JOURNALNOTIFY message to the journal record hook function whenever an application calls WinGetPhysKeyState or WinQueryQueueStatus. This message is necessary because the system-message queue is only one message deep while a playback hook is active. For example, the user might press the A, B, and C keys while in record mode. While the application is processing the A character message, the B key might be down; WinGetPhysKeyState returns this information. However, during playback mode, the system knows only that it currently is processing the A key.
HK_LOADER - Loader Hook
The loader hook allows the library and procedure loading and deleting calls to be intercepted. The following code shows the syntax for a loader hook function:
BOOL EXPENTRY LoaderHook(HAB hab, LONG idContext, PSZ pszLibname, PHLIB hlib, PSZ pszProcname, PFNWP wndProc);
If the hook attempts a load or deletion which is unsuccessful, then the hook must establish the relevant error information. The hab parameter is the anchor block handle. The idContext parameter is the origin of the call to the hook:
LHK_DELETEPROC WinDeleteProcedure LHK_DELETELIB WinDeleteLibrary LHK_LOADPROC WinLoadProcedure LHK_LOADLIB WinLoadLibrary
The pszLibname parameter is the library name. The hlib parameter is a pointer to a library handle. If the idContext parameter is set to LHK_LOADLIB, then this hook must set the value of this parameter to the handle of the loaded library or to NULLHANDLE if the load fails. The pszProcname parameter is the procedure name. The wndProc parameter is the window procedure identifier. If the idContext parameter is set LHK_LOADPROC, then this hook must set the value of this parameter to the handle of the loaded procedure or to NULL if the load fails. The pfSuccess parameter is the success indicator, which is either TRUE or FALSE. If it is TRUE, the library or procedure loaded or deleted successfully. If it is FALSE, the library or procedure not loaded or deleted successfully. When this function returns TRUE, it does not call the next hook in chain. When this function returns FALSE, then it does call the next hook in chain.
HK_LOCKUP - Lockup Hook
The lockup hook is called when the system locks itself up. The following code shows the syntax for a lockup hook function:
BOOL EXPENTRY LockupHook (HAB hab, HWND hwndLockupFrame);
The hab parameter is the application anchor block. The hwndLockupFrame parameter is the frame window of the lockup panel. This function has no return value. All HK_LOCKUP hooks registered with the system are called when the system locks itself up. All HK_LOCKUP hooks must be system hooks, not message queue hooks. Application programs that create other lockup password input windows by hooking the HK_LOCKUP system hook can use WinUnlockSystem to force the system to unlock when another form of input is detected other than mouse or keyboard. For example, a pen gesture or some voice input or signature recognition.
HK_MSGCONTROL - Message Control Hook
The message control hook allows the application to determine the flow of messages to be intercepted. The following code shows the syntax for a message control hook function:
BOOL EXPENTRY MsgControlHook(HAB hab, LONG/SHORT idContext, HWND hwnd, PSZ pszClassname, ULONG/USHORT usMsgclass, LONG/SHORT idControl, PBOOL fSuccess);
If the hook is unable to alter the message control state, then the hook must establish the relevant error information. The hab parameter is the anchor block handle. The idContext parameter is the origin of the call to the hook and has one of the following values:
MCHK_CLASSMSGINTEREST WinSetClassMsgInterest MCHK_MSGINTEREST WinSetMsgInterest MCHK_MSGMODE WinSetMsgMode MCHK_SYNCHRONISATION WinSetSynchroMode
The hwnd parameter is the window handle. The pszClassName parameter is the window class name. The usMsgClass parameter is the message class. The idControl parameter is the control setting. The fSuccess parameter is the success indicator and is either TRUE (success) or FALSE (error). This function returns either TRUE or FALSE. If it returns TRUE, the next hook in the chain is not called. If it returns FALSE, the next hook in the chain is called.
HK_MSGFILTER - Message Filter Hook
The message-filter hook allows an application to provide input filtering (such as monitoring hot keys) during system-modal loops. The system calls a message-filter hook function while tracking the window size and movement, displaying a modal dialog window or message box, tracking a scroll bar, and during window-enumeration operations. The following code shows the syntax for a message-filter hook function:
BOOL EXPENTRY MsgFilterHook(HAB hab, PQMSG pQmsg, ULONG msgf)
The msgf parameter can have one of the three values shown in the following table:
Parameter Value | Description |
---|---|
MSGF_DIALOGBOX | Message originated while processing a modal dialog window or a message box. |
MSGF_MESSAGEBOX | Message originated while processing a message box. |
MSGF_TRACK | Message originated while tracking a control (such as a scroll bar). |
The pQmsg parameter of MsgFilterHook is a pointer to a QMSG data structure containing information about the message.
If a message-filter hook function returns TRUE, the system does not pass the message to the rest of the hook chain or to the application. If the function returns FALSE, the system passes the message to the next hook function in the chain or to the application if no other functions exist.
This hook enables applications to perform message filtering during modal loops that is equivalent to the typical filtering for the main message loop. For example, applications often examine a new message in the main event loop between the time they retrieve the message from the queue and the time they dispatch it, performing special processing as appropriate. An application usually cannot do this sort of filtering during a modal loop, because the system runs the loop created by WinGetMsg and WinDispatchMsg. If an application installs a message-filter hook function, the system calls the function between WinGetMsg and WinDispatchMsg in the modal processing loop.
An application can also call the message-filter hook function directly by calling WinCallMsgFilter. With this function, the application can use the same code as the main message loop to filter messages during modal loops. To do so, the application encapsulates the filtering operations in a message-filter hook function and calls WinCallMsgFilter between WinGetMsg and WinDispatchMsg calls, as shown in the following code fragment:
while (WinGetMsg(hab, (PQMSG) &qmsg, (HWND) NULL, 0, 0)) { if (!WinCallMsgFilter(hab, (PQMSG) &qmsg, 0)) WinDispatchMsg(hab, (PQMSG) &qmsg); }
The last argument of WinCallMsgFilter is passed to the hook function; the application can enter any value. By defining a constant such as MSGF_MAINLOOP, the hook function can use that value to determine from where the function was called.
HK_MSGINPUT - Message Input Hook
The message input hook is intended for simulated user input, and only mouse and keyboard messages should be passed in. All other messages will be discarded. Mouse and keyboard messages injected into this hook will have the same effect as if they were generated by the mouse or keyboard device driver. The messages are routed in the same manner as normal user input. The following code shows the syntax for a message input hook function:
BOOL EXPENTRY MsgInputHook (HAB hab, PQMSG pQmsg, BOOL fSkip, PBOOL pfNoRecord);
The hab parameter is the anchor block handle. The pQmsg parameter is the queue message data structure to be filled in with a simulated mouse or keyboard message. The fSkip parameter is the skip message flag, which is either TRUE or FALSE. If TRUE, the hook should advance to the next message. If FALSE, the current message should be returned. The pfNoRecord parameter is the record message flag, which is either TRUE or FALSE. If TRUE, then the message will not be recorded in the JournalRecordHook hook. If FALSE, the message will be recorded in the JournalRecordHook hook. This function returns either TRUE or FALSE. If TRUE, the pQmsg structure contains the current message to be passed in for handling. If FALSE, The pQmsg structure was not filled in. There are no further messages to pass in.
HK_PLIST_ENTRY - Program List Call Hook
The program list call hook is called every time a program-list call or initialization file call is invoked by an application. It is called before the call is run. This hook, together with its counterpart, ProgramListExitHook, lets applications or system components do the following:
- Implement the initialization file and program list partially, while retaining the existing implementation. For example, read-only requests could be satisfied from memory, rather than from disk.
- Redirect initialization-file operations on a particular group to an alternative (opened) profile. For example, in a multiple-user environment, a LAN program might choose to redirect profile groups that are hardware-dependent, rather than user-dependent, to the system-initialization file.
The following code shows the syntax for a program list call hook function:
BOOL EXPENTRY ProgramListEntryHook(HAB hab, PPRFHOOKPARMS pProfileHookParams, PBOOL fNoExecute);
The hab parameter is the anchor block handle. The pProfileHookParams is the profile hook parameters. These identify the call and give its parameters and return code. The fNoExecute parameter is the suppress indicator and is either TRUE or FALSE. If set to TRUE by any hook procedure, no further processing of the call is done. If set to FALSE by all hook procedures, the call is processed normally. This function returns either TRUE or FALSE. If TRUE, the next hook in the chain is not called. If FALSE, the next hook in the chain is called.
HK_PLIST_EXIT - Program List Exit Hook
The program list exit hook called every time a program-list call or initialization file call is invoked by an application. It is called before the call is run. This hook, together with its counterpart, ProgramListEntryHook, lets applications or system components:
- Implement the initialization file and program list partially, whilst retaining the existing implementation. For example, read-only requests could be satisfied from memory, rather than from disk.
- Redirect initialization-file operations on a particular group to an alternative (opened) profile. For example, in a multiple-user environment, a LAN program might choose to redirect profile groups that are hardware-dependent, rather than user-dependent, to the system-initialization file.
The following code shows the syntax for a program list exit hook function:
BOOL EXPENTRY ProgramListExitHook(HAB hab, PPRFHOOKPARMS pProfileHookParams);
>
The hab parameter is the anchor block handle. The pProfileHookParams parameter is the profile hook parameters. These identify the call and give its parameters and return code. This function returns either TRUE or FALSE. If it returns TRUE, the next hook in the chain is not called. If it returns FALSE, the next hook in the chain is called.
HK_REGISTERUSERMSG - Register User Message Hook
The register user message hook is called whenever a user message or data type is registered. The following code shows the syntax for a register user message hook function:
BOOL EXPENTRY RegisterUserHook(HAB hab, ULONG cUshort, PULONG/PUSHORT arRMP, PBOOL fRegistered);
The hab parameter is the application anchor block. The cUshort parameter is the number of data type codes. For data types, this parameter is the number of data type codes in the arRMP parameter. This value must not be less than one. For messages, this parameter is set to 0. The arRMP parameter is an array of data type codes. For data types, this parameter is an array of data type codes. For messages, this parameter is set to NULL. Valid data types are the system-defined data types and their pointer equivalents, application-defined data types and their pointer equivalents, and control data types. Note that not all of the data types that occur in the CPI an be specified in this function. A control data type is followed by one or more entries in the arRMP array that are interpreted in a special way. Control data types allow arrays, offsets, and lengths to be defined. See RegisterUserHook for a full description of the values for the arRMP parameter. The fRegistered parameter is the flag indicating that a message or data type was registered. If TRUE, the message or data type was registered. If FALSE, the message or data type was not registered. The function returns either TRUE (success) or FALSE (an error occurred).
HK_SENDMSG - Send Message Hook
The send message hook enables an application to monitor messages that the system does not post to a queue. The system calls a send message hook function while processing WinSendMsg, before delivering the message to the recipient window. By installing an input-hook function and a send message hook function, an application can monitor all window messages effectively. The following code shows the syntax for a send message hook function:
VOID EXPENTRY SendMsgHook(HAB hab, PSMHSTRUCT psmh, BOOL fInterTask);
The psmh parameter is a pointer to an SMHSTRUCT data structure that contains information about the message. The fInterTask parameter is TRUE if the message is sent between two threads, or FALSE if the message is sent within a thread. A send message hook function does not return a value, and the next function in the chain is always called. The function can modify values in the SMHSTRUCT data structure before returning.
HK_WINDOWDC - Device Context Hook
The device context hook is called when a device context is allocated or freed. The following code shows the syntax for a device context hook function:
BOOL EXPENTRY WindowDCHook(HAB hab, HDC hdc, HWND hwnd, BOOL flAssociate);
The hab parameter is the application anchor block. The hdc parameter is the current device-context handle. The hwnd parameter is the current window handle. The flAssociate parameter is the association flag and is either TRUE or FALSE. If TRUE, the device context has been allocated. If FALSE, the device context has been freed. This function returns either TRUE (success) or FALSE (error).
Using Hooks
This section explains how to perform the following tasks:
Install hook functions Release hook functions and free memory Record and play back input events
Note: Most of the sample code in this section is part of a complete program which is shown in "Sample Code for Hooks".
Installing Hook Functions
You can install hook functions by calling WinSetHook, specifying the type of hook that calls the function-whether the function is to be associated with the system-message queue or with the queue of a particular thread-and a pointer to a function entry point. The following sample code shows how to install a hook function into the message queue of a thread:
BOOL EXPENTRY MyInputHook(HAB, PQMSG, USHORT); HAB hab; HMQ hmq; WinSetHook(hab, /* Anchor block handle */ hmq, /* Thread message queue */ HK_INPUT, /* Called by the input hook */ (PFN) MyInputHook, /* Address of input-hook function */ (HMODULE)NULL); /* Function is in appl. module */
Place hook functions associated with the system-message queue in a dynamic link library (DLL) separate from the application that installs the hook function. The installing application needs the handle of the DLL module before it can install the hook function. DosLoadModule, given the name of the DLL, returns the handle of the DLL module. Once you have the handle, you can call DosQueryProcAddr to obtain the address of the hook function. Finally, use WinSetHook to install the hook-function address in the appropriate hook list. WinSetHook passes the module handle, a pointer to the hook-function entry point, and NULL for the message-queue argument, indicating that the hook function should be associated with the system queue. The following sample code shows functions, called from the application's main routine, that initialize a DLL and install the hook function:
HAB habDLL; HMODULE hMod; PFN pfnInput; /***********************************************************************/ /* InitDLL: This function sets up the DLL and sets all variables. */ /***********************************************************************/ void EXPENTRY InitDLL(HAB hab) { habDLL = hab; /***********************************************************************/ /* Load the dll - actually, just get our module handle. */ /***********************************************************************/ DosLoadModule(NULL, 0, "HOOKDLL", &hMod); /***********************************************************************/ /* Find the address of the input hook procedure. */ /***********************************************************************/ DosQueryProcAddr(hMod, 0, "InputProc", &pfnInput); } /***********************************************************************/ /* StartInputHook: This function starts the hook filtering. */ /***********************************************************************/ void EXPENTRY StartInputHook(void) { /***********************************************************************/ /* Set a hook to our input filter routine. */ /***********************************************************************/ WinSetHook(habDLL, NULLHANDLE, HK_INPUT, pfnInput, hMod); }
Releasing Hook Functions
You can release a hook function and remove its address from the hook list by calling WinReleaseHook with the same arguments that you used when installing the hook function, as shown in the following sample code:
BOOL EXPENTRY MyInputHook(HAB, PQMSG, USHORT); HAB hab; HMQ hmq; WinReleaseHook(hab, /* Anchor block handle */ hmq, /* Thread message queue */ HK_INPUT, /* Called by the input hook */ (PFN) MyInputHook, /* Address of input-hook function */ (HMODULE)NULL); /* Function is in appl. module */
Release all hook functions before the application ends, even though the system automatically releases them if the application does not. You also need to free the memory associated with the hook.
Freeing Memory
How memory for the hook is freed depends on the type of hook chain an event is linked to:
- Queue (current) hook chain
- System hook chain
A queue hook chain is a private hook chain. It applies only to the current calling thread that created the queue with which the hook chain is associated. It may or may not reside in a DLL. If it is not associated with a DLL, its memory can be freed by WinReleaseHook, as shown in the previous sample code.
A system hook chain must reside in a DLL; therefore, it affects the entire system. WinSetHook allocates memory and associates it with a DLL. This memory is not freed until the DLL module is freed. WinReleaseHook cannot free the DLL's memory, because another process cannot free the DLL and its associated memory. However, this memory can be freed by launching a thread that does the following:
- Loads the DLL and sets the hook
- When the playback sequence is complete, releases the hook and frees the DLL, thus relinquishing its memory
As long as any DLL associated with the hook is alive, WinReleaseHook cannot free the memory.
The implication here is straightforward:
- If a queue hook is being installed and it is not associated with a DLL, WinReleaseHook can free its memory.
- If a system hook is being installed, its memory cannot be freed until the DLL is freed. WinReleaseHook has to do a DosFreeModule, but it cannot do this for another process. The application must use DosFreeModule to relinquish hook-allocated memory associated with a DLL.
The following sample code shows a function, called from an application's main routine, that releases the hook and frees the memory of the hook installed in a sample code shown earlier.
/***********************************************************************/ /* StopInputHook: This function stops the hook filtering. */ /***********************************************************************/ void EXPENTRY StopInputHook(void) { /***********************************************************************/ /* Drop a hook to our input filter routine. */ /***********************************************************************/ WinReleaseHook(habDLL, NULLHANDLE, HK_INPUT, pfnInput, hMod); /***********************************************************************/ /* Decrement the DLL usage count. */ /***********************************************************************/ DosFreeModule(hMod); }
Recording and Playing Back Input Events
To record and play back input events, use the journal record hook to create a local queue to store the recorded events, then use the journal playback hook to create a second thread to read from the queue. Do not attempt to spend any significant cycles within JournalRecordHook. Because the recorded events include semaphores, Win calls, and I/O functions, it can cause system deadlocks. The following pseudocode describes how to play back recorded functions:
Store the passed time as the current time If the system requests a new message to be prepared (skip is TRUE) If all messages have been played back, release the Playback Hook (After release, your playback hook function will still be called a few times more. So leave a null mouse move message as the next message to be copied.) Otherwise: Save the last message time Copy the new message to the passed qmsg buffer Calculate the time until the next message (You should know, from the recorded times, the delta time which actually occurred between each message. During playback you will need to calculate the amount of time remaining between the time passed to you in the qmsg buffer, i.e., "current time", and the time at which the next message is due to be kicked off.) Otherwise (skip is FALSE, so the system wants a peek at the current message): Copy the existing (current) message to the passed qmsg buffer Recalculate and return the REMAINING delay for the current message
An alternative method for installing a system-queue hook function is to provide an installation function in the DLL along with the hook function. With this method, the installing application does not need the handle of the DLL module. By linking with the DLL, the application gains access to the installation function, which can supply the DLL module handle and other details in the call to WinSetHook. The DLL can also contain a function that releases the system-queue hook function. The application can call this hook-releasing function when it ends.
Sample Code for Hooks
This section illustrates a complete hook sample program. Several parts of this program are explained in "Using Hooks".
Hooks Application Sample Code
The hook application includes the following files:
- Hookdemo.C
- Hookdll.C
- Hookdemo.RC
- Hookdemo.H
- Hookdemo.DEF
- Hookdemo.LNK
- Hookdll.DEF
- Hookdll.LNK
- Hookdemo.MAK
The following sample illustrates the hook application code:
============HOOKDEMO.C============ #define INCL_WIN #define INCL_GPI #include <os2.h> #include "hookdemo.h" #pragma linkage (main,optlink) INT main(VOID); /***********************************************************************/ /* Main() - program entry point. */ /***********************************************************************/ MRESULT EXPENTRY LocalWndProc(HWND, ULONG, MPARAM, MPARAM); HAB hab; HWND hFrameWnd; PFNWP SysWndProc; INT main (VOID) { HMQ hmq; FRAMECDATA fcd; QMSG qmsg; if (!(hab = WinInitialize(0))) return FALSE; /***********************************************************************/ /* Initialize our DLL, which holds the system hook routines. */ /***********************************************************************/ InitDLL(hab); if (!(hmq = WinCreateMsgQueue(hab, 0))) return FALSE; /***********************************************************************/ /* Setup the frame control data for the frame window. */ /***********************************************************************/ fcd.cb = sizeof(FRAMECDATA); fcd.flCreateFlags = FCF_TITLEBAR | FCF_SYSMENU | FCF_MENU | FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_MINMAX | FCF_TASKLIST; fcd.hmodResources = NULLHANDLE; fcd.idResources = HOOKDEMO; /***********************************************************************/ /* Create the frame - it will hold the container control. */ /***********************************************************************/ hFrameWnd = WinCreateWindow(HWND_DESKTOP, WC_FRAME, "HookDemo", 0, 0, 0, 0, 0, NULLHANDLE, HWND_TOP, 0, &fcd, NULL); /***********************************************************************/ /* Verify that the frame was created; otherwise, stop. */ /***********************************************************************/ if (!hFrameWnd) return FALSE; /***********************************************************************/ /* Set an icon for the frame window. */ /***********************************************************************/ WinSendMsg(hFrameWnd, WM_SETICON, (MPARAM)WinQuerySysPointer(HWND_DESKTOP, SPTR_FOLDER, FALSE), NULL); /***********************************************************************/ /* We must intercept the frame window's messages. */ /* We save the return value (the current WndProc), */ /* so we can pass it all the other messages the frame gets. */ /***********************************************************************/ SysWndProc = WinSubclassWindow(hFrameWnd, (PFNWP)LocalWndProc); WinShowWindow(hFrameWnd,TRUE); /***********************************************************************/ /* Standard PM message loop - get it, dispatch it. */ /***********************************************************************/ while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0)) { WinDispatchMsg(hab, &qmsg); } /***********************************************************************/ /* Clean up on the way out. */ /***********************************************************************/ WinDestroyWindow(hFrameWnd); WinDestroyMsgQueue(hmq); WinTerminate(hab); return TRUE; } /***********************************************************************/ /* LocalWndProc() - window procedure for the frame window. */ /* Called by PM whenever a message is sent to the frame. */ /***********************************************************************/ MRESULT EXPENTRY LocalWndProc(HWND hwnd,ULONG msg,MPARAM mp1,MPARAM mp2) { char szBuffer[80]; POINTL pt; int x; switch(msg) { /***********************************************************************/ /* Send the message to the usual WC_FRAME WndProc. */ /***********************************************************************/ case WM_COMMAND: switch (SHORT1FROMMP(mp1)) { /***********************************************************************/ /* Start the hook routine - it stops all WM_COMMAND messages. */ /* (which means all these other messages will be ignored). */ /***********************************************************************/ case IDM_START: StartInputHook(); break; case IDM_STOP: StopInputHook(); break; case IDM_EXIT: WinPostMsg(hwnd, WM_CLOSE, 0, 0); break; default: return (*SysWndProc)(hwnd, msg, mp1, mp2); } break; /***********************************************************************/ /* Send the message to the usual WC_FRAME WndProc. */ /***********************************************************************/ default: return (*SysWndProc)(hwnd, msg, mp1, mp2); break; } return FALSE; } ============ HOOKDLL.C ============ #define INCL_WIN #define INCL_DOS #include <os2.h> /***********************************************************************/ /* Global variables. */ /***********************************************************************/ HAB habDLL; HMODULE hMod; PFN pfnInput; /***********************************************************************/ /* InitDLL: This function sets up the DLL and sets all variables */ /***********************************************************************/ void EXPENTRY InitDLL(HAB hab) { habDLL = hab; /***********************************************************************/ /* Load the DLL - actually, just get our module handle. */ /***********************************************************************/ DosLoadModule(NULL, 0, "HOOKDLL", &hMod); /***********************************************************************/ /* Find the address of the input hook procedure. */ /***********************************************************************/ DosQueryProcAddr(hMod, 0, "InputProc", &pfnInput); } /***********************************************************************/ /* StartInputHook: This function starts the hook filtering. */ /***********************************************************************/ void EXPENTRY StartInputHook(void) { /***********************************************************************/ /* Set a hook to our input filter routine. */ /***********************************************************************/ WinSetHook(habDLL, NULLHANDLE, HK_INPUT, pfnInput, hMod); } /***********************************************************************/ /* StopInputHook: This function stops the hook filtering. */ /***********************************************************************/ void EXPENTRY StopInputHook(void) { /***********************************************************************/ /* Drop a hook to our input filter routine. */ /***********************************************************************/ WinReleaseHook(habDLL, NULLHANDLE, HK_INPUT, pfnInput, hMod); /***********************************************************************/ /* Decrement the DLL usage count. */ /***********************************************************************/ DosFreeModule(hMod); } /***********************************************************************/ /* InputProc: This is the input filter routine. */ /* While the hook is active, all messages come here */ /* before being dispatched. */ /***********************************************************************/ BOOL EXPENTRY InputProc(HAB hab, PQMSG pqMsg, ULONG fs) { /***********************************************************************/ /* Check for WM_COMMAND messages. */ /***********************************************************************/ if (pqMsg->msg == WM_COMMAND) { /***********************************************************************/ /* Ignore all WM_COMMAND messages (stops menu processing). */ /***********************************************************************/ return TRUE; } /***********************************************************************/ /* Pass the message on to the next hook in line. */ /***********************************************************************/ return FALSE; } ============ HOOKDEMO.RC ============ #include <os2.h> #include "hookdemo.h" MENU HOOKDEMO BEGIN SUBMENU "Command", IDM_CMD BEGIN MENUITEM "Start", IDM_START MENUITEM "Stop", IDM_STOP MENUITEM "Exit", IDM_EXIT END END ============ HOOKDEMO.H ============ #define HOOKDEMO 256 #define IDM_CMD 400 #define IDM_START 401 #define IDM_STOP 402 #define IDM_EXIT 403 ============ HOOKDEMO.DEF ============ NAME HOOKDEMO WINDOWAPI DESCRIPTION 'PM Hooks Sample' CODE MOVEABLE DATA MOVEABLE MULTIPLE STACKSIZE 24576 HEAPSIZE 10240 PROTMODE ============ HOOKDEMO.LNK ============ hookdemo.obj /NOI hookdemo.exe hookdemo.map hookdll.lib hookdemo.def ============ HOOKDLL.DEF ============ LIBRARY HOOKDLL DESCRIPTION 'PM Hooks Sample' CODE LOADONCALL DATA LOADONCALL PROTMODE EXPORTS InitDLL StartInputHook StopInputHook InputProc ============ HOOKDLL.LNK ============ hookdll.obj /NOI hookdll.dll hookdll.map hookdll.def ============ HOOKDEMO.MAK ============ CC = icc /c /Ge /Gd- /Se /Re /ss /Gm+ LINK = link386 HEADERS = hookdemo.h #------------------------------------------------------------------- # A list of all of the object files. #------------------------------------------------------------------- ALL_OBJ1 = hookdemo.obj ALL_OBJ2 = hookdll.obj all: hookdemo.exe hookdll.dll hookdemo.res: hookdemo.rc hookdemo.h hookdemo.obj: hookdemo.c $(HEADERS) icc /C /Ss /W3 hookdemo.c hookdll.obj: hookdll.c icc /C+ /Ge- /Gm+ hookdll.c hookdll.dll: $(ALL_OBJ2) hookdll.def hookdll.lnk $(LINK) @hookdll.lnk implib hookdll.lib hookdll.def hookdemo.exe: $(ALL_OBJ1) hookdemo.def hookdemo.lnk hookdemo.res hook dll.lib $(LINK) @hookdemo.lnk rc -p -x hookdemo.res hookdemo.exe