Jump to content

PMGuide - Hooks: Difference between revisions

From EDM2
No edit summary
 
(6 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{IBM-Reprint}}
{{PMGuide}}
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.
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.


Line 21: Line 23:
|-
|-
| HK_CHECKMSGFILTER
| HK_CHECKMSGFILTER
| Lets applications apply very specific message filtering. See [[HK_CHECKMSGFILTER - Check Message Filter Hook]].
| Lets applications apply very specific message filtering. See HK_CHECKMSGFILTER - Check Message Filter Hook.
|-
|-
| HK_CODEPAGECHANGED
| HK_CODEPAGECHANGED
| Lets applications determine when the code page changes. See [[HK_CODEPAGECHANGE - Code Page Changed Hook]].
| Lets applications determine when the code page changes. See HK_CODEPAGECHANGE - Code Page Changed Hook.
|-
|-
| HK_DESTROYWINDOW
| HK_DESTROYWINDOW
| Called whenever a window is destroyed. See [[HK_DESTROYWINDOW - Destroy Window Hook]].
| Called whenever a window is destroyed. See HK_DESTROYWINDOW - Destroy Window Hook.
|-
|-
| HK_FINDWORD
| HK_FINDWORD
| Lets applications control where WinDrawText places line breaks. See [[HK_FINDWORD - Find Word Hook]].
| Lets applications control where WinDrawText places line breaks. See HK_FINDWORD - Find Word Hook.
|-
|-
| HK_FLUSHBUF
| HK_FLUSHBUF
| Lets applications save data before the system reboots. See [[HK_FLUSHBUF - Flush Buffer Hook]].
| Lets applications save data before the system reboots. See HK_FLUSHBUF - Flush Buffer Hook.
|-
|-
| HK_HELP
| HK_HELP
| Monitors the WM_HELP message. See [[HK_HELP - Help Hook]].
| Monitors the WM_HELP message. See HK_HELP - Help Hook.
|-
|-
| HK_INPUT
| HK_INPUT
| Monitors messages in the specified message queue. See [[HK_INPUT - Input Hook]].
| Monitors messages in the specified message queue. See HK_INPUT - Input Hook.
|-
|-
| HK_JOURNALPLAYBACK
| HK_JOURNALPLAYBACK
| Lets applications insert messages into the system message queue. See [[HK_JOURNALPLAYBACK - Journal Playback Hook]].
| Lets applications insert messages into the system message queue. See HK_JOURNALPLAYBACK - Journal Playback Hook.
|-
|-
| HK_JOURNALRECORD
| HK_JOURNALRECORD
| Lets applications record mouse and keyboard input messages. See [[HK_JOURNALRECORD - Journal Record Hook]].
| Lets applications record mouse and keyboard input messages. See HK_JOURNALRECORD - Journal Record Hook.
|-
|-
| HK_LOADER
| HK_LOADER
| Lets the library and procedure loading and deleting calls be intercepted. See [[HK_LOADER - Loader Hook]].
| Lets the library and procedure loading and deleting calls be intercepted. See HK_LOADER - Loader Hook.
|-
|-
| HK_LOCKUP
| HK_LOCKUP
| Called when the system locks itself up. See [[HK_LOCKUP - Lockup Hook]].
| Called when the system locks itself up. See HK_LOCKUP - Lockup Hook.
|-
|-
| HK_MSGCONTROL
| HK_MSGCONTROL
| Monitors the flow of messages to be intercepted. See [[HK_MSGCONTROL - Message Control Hook]].
| Monitors the flow of messages to be intercepted. See HK_MSGCONTROL - Message Control Hook.
|-
|-
| HK_MSGFILTER
| HK_MSGFILTER
| Monitors input events during system modal loops. See [[HK_MSGFILTER - Message Filter Hook]].
| Monitors input events during system modal loops. See HK_MSGFILTER - Message Filter Hook.
|-
|-
| HK_MSGINPUT
| 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]].
| 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
| 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]].
| 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
| 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]].
| 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
| HK_REGISTERUSERMSG
| Called whenever a user message or data type is registered. See [[HK_REGISTERUSERMSG - Register User Message Hook]].
| Called whenever a user message or data type is registered. See HK_REGISTERUSERMSG - Register User Message Hook.
|-
|-
| HK_SENDMSG
| HK_SENDMSG
| Monitors messages sent by using WinSendMsg. See [[HK_SENDMSG - Send Message Hook]].
| Monitors messages sent by using WinSendMsg. See HK_SENDMSG - Send Message Hook.
|-
|-
| HK_WINDOWDC
| HK_WINDOWDC
| Called when a device context is allocated or freed. See [[HK_WINDOWDC - Device Context Hook]].
| Called when a device context is allocated or freed. See HK_WINDOWDC - Device Context Hook.
|}
|}


Line 86: Line 88:
=== Hook Types ===
=== 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.
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.




Line 168: Line 170:


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.
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.
{{Note|Do not use any of the Win or Gpi functions inside the hook routine. At the point in time that the hook routine is running, these subsystems might not be fully available because they might be partially shutdown.}}
 
The '''hab''' parameter is the application anchor block.
;Note:Do not use any of the Win or Gpi functions inside the hook routine. At the point in time that the hook routine is running, these subsystems might not be fully available because they might be partially shutdown.
When this function returns TRUE, the function completed successfully.
 
When this function returns FALSE, an error occurred.
*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 ====
==== HK_HELP - Help Hook ====
Line 221: Line 225:
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:
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:


<pre>
<pre>
BOOL EXPENTRY InputHook(HAB hab, PQMSG pQmsg, ULONG fs)
BOOL EXPENTRY InputHook(HAB hab, PQMSG pQmsg, ULONG fs)
&lt;/pre>
</pre>


The '''pQmsg''' parameter is a pointer to a QMSG data structure that contains information about the message.
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:
The '''fs''' parameter of InputHook can contain the following flags from WinPeekMsg, indicating whether or not the message is removed from the queue:


&lt;pre>
<pre>
PM_NOREMOVE
PM_NOREMOVE
PM_REMOVE
PM_REMOVE
&lt;/pre>
</pre>


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.
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.
Line 244: Line 248:
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:
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:


&lt;pre>
<pre>
ULONG EXPENTRY JournalPlaybackHook(HAB hab, BOOL fSkip,
ULONG EXPENTRY JournalPlaybackHook(HAB hab, BOOL fSkip,
PQMSG pQmsg)
PQMSG pQmsg)
&lt;/pre>
</pre>


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 '''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.
Line 257: Line 261:
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:
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:


&lt;pre>
<pre>
VOID EXPENTRY JournalRecordHook(HAB hab, PQMSG pQmsg)
VOID EXPENTRY JournalRecordHook(HAB hab, PQMSG pQmsg)
&lt;/pre>
</pre>


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.
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.
Line 265: Line 269:
The following messages are passed to the journal record hook:
The following messages are passed to the journal record hook:


&lt;pre>
<pre>
WM_CHAR
WM_CHAR
WM_BUTTON1DOWN
WM_BUTTON1DOWN
Line 274: Line 278:
WM_BUTTON3UP
WM_BUTTON3UP
WM_MOUSEMOVE.
WM_MOUSEMOVE.
&lt;/pre>
</pre>


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 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.
Line 283: Line 287:
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:
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:


&lt;pre>
<pre>
BOOL EXPENTRY LoaderHook(HAB  hab,
BOOL EXPENTRY LoaderHook(HAB  hab,
LONG  idContext,
LONG  idContext,
Line 290: Line 294:
PSZ  pszProcname,
PSZ  pszProcname,
PFNWP wndProc);
PFNWP wndProc);
&lt;/pre>
</pre>


If the hook attempts a load or deletion which is unsuccessful, then the hook must establish the relevant error information.
If the hook attempts a load or deletion which is unsuccessful, then the hook must establish the relevant error information.
Line 314: Line 318:
The ''lockup hook'' is called when the system locks itself up. The following code shows the syntax for a lockup hook function:
The ''lockup hook'' is called when the system locks itself up. The following code shows the syntax for a lockup hook function:


&lt;pre>
<pre>
BOOL EXPENTRY LockupHook (HAB  hab,
BOOL EXPENTRY LockupHook (HAB  hab,
HWND hwndLockupFrame);
HWND hwndLockupFrame);
&lt;/pre>
</pre>


The '''hab''' parameter is the application anchor block.
The '''hab''' parameter is the application anchor block.
Line 381: Line 385:


The '''pQmsg''' parameter of MsgFilterHook is a pointer to a QMSG data structure containing information about the message.
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.
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.
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:   
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:   


Line 391: Line 398:
WinDispatchMsg(hab, (PQMSG) &amp;qmsg);
WinDispatchMsg(hab, (PQMSG) &amp;qmsg);
}
}
</pre>
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:
<pre>
BOOL EXPENTRY MsgInputHook (HAB  hab,
                                  PQMSG pQmsg,
                                  BOOL fSkip,
                                  PBOOL pfNoRecord);
</pre>
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:
<pre>
BOOL EXPENTRY ProgramListEntryHook(HAB hab,
                                  PPRFHOOKPARMS pProfileHookParams,
                                  PBOOL fNoExecute);
</pre>
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:
<pre>
<pre>
BOOL EXPENTRY ProgramListExitHook(HAB hab,
                                  PPRFHOOKPARMS pProfileHookParams);
</pre>>
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.


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_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:
 
<pre>
BOOL EXPENTRY RegisterUserHook(HAB hab,
                                  ULONG cUshort,
                                  PULONG/PUSHORT arRMP,
                                  PBOOL fRegistered);
</pre>
 
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:
 
<pre>
VOID EXPENTRY SendMsgHook(HAB hab,
                                  PSMHSTRUCT psmh,
                                  BOOL fInterTask);
</pre>
 
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:
 
<pre>
BOOL EXPENTRY WindowDCHook(HAB  hab,
                                  HDC  hdc,
                                  HWND hwnd,
                                  BOOL flAssociate);
</pre>
 
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:
<pre>
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    */
</pre>
 
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:
<pre>
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);
}
</pre>
 
=== 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:
<pre>
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    */
</pre>
 
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.
<pre>
/***********************************************************************/
/* 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);
}
</pre>
 
==== 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:
<pre>
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
</pre>
 
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:
<PRE>
============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
 
</PRE>

Latest revision as of 03:13, 29 April 2025

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

Presentation Manager Programming Guide and Reference
  1. How to Use this Book
  2. Device Functions
  3. Direct Manipulation Functions
  4. Dynamic Data Formatting Functions
  5. Hooks and Procedures
  6. Profile Functions
  7. Spooler Functions
  8. Window Functions
  9. Message Processing
  10. Data Types
  11. Errors
  12. Atom Tables
  13. Button Controls
  14. Clipboards
  15. Combination Box
  16. Container Controls
  17. Control Windows
  18. Cursors
  19. Dialog Windows
  20. Direct Manipulation
  21. Drawing in Windows
  22. Dynamic Data Exchange
  23. Entry-Field Controls
  24. File Dialog Controls
  25. Font Dialog Controls
  26. Frame Windows
  27. Hooks
  28. Initialization Files
  29. Keyboard Accelerators
  30. List-Box Controls
  31. Menus
  32. Messages and Message Queues
  33. Multiple-Line Entry Field Controls
  34. Mouse and Keyboard Input
  35. Mouse Pointers and Icons
  36. Notebook Controls
  37. Painting and Drawing
  38. Presentation Parameters
  39. Resource Files
  40. Scroll-Bar Controls
  41. Slider Controls
  42. Spin Button Controls
  43. Static Controls
  44. Title-Bar Controls
  45. Value Set Controls
  46. Windows
  47. Window Classes
  48. Window Procedures
  49. Window Timers
  50. Appendices
  51. Notices
  52. Glossary

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.

Note
Do not use any of the Win or Gpi functions inside the hook routine. At the point in time that the hook routine is running, these subsystems might not be fully available because they might be partially shutdown.
  • 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:

  1. 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.
  2. 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:

  1. 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.
  2. 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