Windows User Interface Technical Articles
Win32 Hooks
 

Kyle Marsh
Microsoft Developer Network Technology Group

Created: July 29, 1993

Revised: February 1994
Added exception for journal hooks in "Filter functions in DLLs" section.
Added .EXE file to where filters can reside in "WH_JOURNALRECORD" and "WH_JOURNALPLAYBACK" sections.
Changed HIWORD and LOWORD to HIBYTE and LOBYTE in "HC_ACTION" section.


Abstract

This article describes hooks and their use in the Microsoft® Win32® application programming interface (API). It discusses hook functions, filter functions, and the following types of hooks:

  • WH_CALLWNDPROC
  • WH_CBT
  • WH_DEBUG
  • WH_FOREGROUNDIDLE
  • WH_GETMESSAGE
  • WH_JOURNALPLAYBACK
  • WH_JOURNALRECORD
  • WH_KEYBOARD
  • WH_MOUSE
  • WH_MSGFILTER
  • WH_SHELL
  • WH_SYSMSGFILTER
Terminology   In this article, the term Windows refers to the Windows family of operating systems, that is, 16-bit Windows, Windows NT®, and Windows for Workgroups. Likewise, Windows 3.1 refers to the 3.1 version of these operating systems.

Introduction

In the Microsoft® Windows® operating system, a hook is a mechanism by which a function can intercept events (messages, mouse actions, keystrokes) before they reach an application. The function can act on events and, in some cases, modify or discard them. Functions that receive events are called filter functions and are classified according to the type of event they intercept. For example, a filter function might want to receive all keyboard or mouse events. For Windows to call a filter function, the filter function must be installed—that is, attached—to a Windows hook (for example, to a keyboard hook). Attaching one or more filter functions to a hook is known as setting a hook. If a hook has more than one filter function attached, Windows maintains a chain of filter functions. The most recently installed function is at the beginning of the chain, and the least recently installed function is at the end.

When a hook has one or more filter functions attached and an event occurs that triggers the hook, Windows calls the first filter function in the filter function chain. This action is known as calling the hook. For example, if a filter function is attached to the CBT hook and an event that triggers the hook occurs (for example, a window is about to be created), Windows calls the CBT hook by calling the first function in the filter function chain.

To maintain and access filter functions, applications use the SetWindowsHookEx and the UnhookWindowsHookEx functions.

Hooks provide powerful capabilities for Windows-based applications. These applications can use hooks to:

  • Process or modify all messages meant for all the dialog boxes, message boxes, scroll bars, or menus for an application (WH_MSGFILTER).
  • Process or modify all messages meant for all the dialog boxes, message boxes, scroll bars, or menus for the system (WH_SYSMSGFILTER).
  • Process or modify all messages (of any type) for the system whenever a GetMessage or a PeekMessage function is called (WH_GETMESSAGE).
  • Process or modify all messages (of any type) whenever a SendMessage function is called (WH_CALLWNDPROC).
  • Record or play back keyboard and mouse events (WH_JOURNALRECORD, WH_JOURNALPLAYBACK).
  • Process, modify, or remove keyboard events (WH_KEYBOARD).
  • Process, modify, or discard mouse events (WH_MOUSE).
  • Respond to certain system actions, making it possible to develop computer-based training (CBT) for applications (WH_CBT).
  • Prevent another filter from being called (WH_DEBUG).

Applications have used hooks to:

  • Provide F1 help key support to menus, dialog boxes, and message boxes (WH_MSGFILTER).
  • Provide mouse and keystroke record and playback features, often referred to as macros. For example, the Windows Recorder accessory program uses hooks to supply record and playback functionality (WH_JOURNALRECORD, WH_JOURNALPLAYBACK).
  • Monitor messages to determine which messages are being sent to a particular window or which actions a message generates (WH_GETMESSAGE, WH_CALLWNDPROC). The Spy utility program in the Platform SDK uses hooks to perform these tasks. The source for Spy is available in the SDK.
  • Simulate mouse and keyboard input (WH_JOURNALPLAYBACK). Hooks provide the only reliable way to simulate these activities. If you try to simulate these events by sending or posting messages, Windows internals do not update the keyboard or mouse state, which can lead to unexpected behavior. If hooks are used to play back keyboard or mouse events, these events are processed exactly like real keyboard or mouse events. Microsoft Excel uses hooks to implement its SEND.KEYS macro function.
  • Provide CBT for applications that run in the Windows environment (WH_CBT). The WH_CBT hook makes developing CBT applications much easier.

How to Use Hooks

To use hooks, you need to know:

  • How to use the Windows hook functions to add and remove filter functions to and from a hook's filter function chain.
  • What action the filter function you are installing will be required to perform.
  • What kinds of hooks are available, what they can do, and what information (parameters) they pass to your filter function.

Windows Hook Functions

Windows-based applications use the SetWindowsHookEx, UnhookWindowsHookEx, and CallNextHookEx functions to manage the hooks filter function chain. Before version 3.1, Windows implemented hook management with the SetWindowsHook, UnhookWindowsHook, and DefHookProc functions. Although these functions are implemented in Win32, they have fewer capabilities than the new (Ex) versions. Please convert your existing code to the new versions of these functions, and always use the new functions for new code.

SetWindowsHookEx and UnhookWindowsHookEx are described below. See "Calling the next function in the filter function chain" for a discussion of CallNextHookEx.

SetWindowsHookEx

The SetWindowsHookEx function adds a filter function to a hook. This function takes four arguments:

  • An integer code describing the hook to which to attach the filter function, and the address of the filter function. These codes are defined in WINUSER.H and are described later.
  • The address of the filter function. The filter function must be exported by including it in the EXPORTS statement in the module definition file for the application or dynamic-link library (DLL), or by using the appropriate compiler flags.
  • The instance handle of the module containing the filter function. In Win32 (unlike Win16), this value should be NULL when installing a thread-specific hook (see below), but does not have to be NULL as the documentation states. When you install a systemwide hook or a thread-specific hook for a thread in another process, you must use the instance handle of the DLL where the filter function resides.
  • The thread ID for which the hook is to be installed. If the thread ID is not zero, the installed filter function will be called only in the context of the specified thread. If the thread ID is zero, the installed filter function has system scope and may be called in the context of any thread in the system. An application or library can use the GetCurrentThreadId function to obtain the thread handle for hooking the current thread.

    Some hooks may be set with system scope only; some may be set only for a specific thread; and others may have either system or thread scope, as shown in the following table.

Hook Scope
WH_CALLWNDPROC Thread or System
WH_CBT Thread or System
WH_DEBUG Thread or System
WH_GETMESSAGE Thread or System
WH_JOURNALRECORD System Only
WH_JOURNALPLAYBACK System Only
WH_FOREGROUNDIDLE Thread or System
WH_SHELL Thread or System
WH_KEYBOARD Thread or System
WH_MOUSE Thread or System
WH_MSGFILTER Thread or System
WH_SYSMSGFILTER System Only

For a given hook type, thread hooks are called first, followed by system hooks.

It is a good idea to use thread hooks instead of system hooks for several reasons. Thread hooks:

  • Do not incur a systemwide overhead in applications that are not interested in the call.
  • Do not cause all events for a hook to be serialized. For example, if an application installs a systemwide keyboard hook, all keyboard messages for all applications will be funneled through that application's keyboard filter function, effectively wasting the system's multiple input queue functionality. If that filter function stops processing keyboard events, the system will appear stopped to the user, but it will not really be stopped. The user can always use the CTRL+ALT+DEL key combination to log out and solve the problem, but he or she will probably not be happy with all this hassle. Also, users may not realize that they can reset the system with the logout/logon sequence.
  • Do not require packaging the filter function implementation in a separate DLL. All systemwide hooks and hooks for threads in different applications must reside in DLLs.
  • Do not need to share data within a DLL that is attached to different processes. A systemwide filter function, which must reside in a DLL, must also share any data that it needs with other processes. Since this is not default DLL behavior, you must be careful when implementing systemwide filter functions. If a filter function is not correctly developed to share data and uses data in a process in which the data is invalid, the process may crash.

SetWindowsHookEx returns a handle to the installed hook (an HHOOK). The application or library must use this handle to identify this hook later when it calls the UnhookWindowsHookEx function. SetWindowsHookEx returns NULL if it is unable to add the filter function to the hook. SetWindowsHookEx also sets the last error to one of the values listed below to indicate why the function failed.

  • ERROR_INVALID_HOOK_FILTER: The hook code is invalid.
  • ERROR_INVALID_FILTER_PROC: The filter function is invalid.
  • ERROR_HOOK_NEEDS_HMOD: A global hook is being set with a NULL hInstance parameter or a thread-specific hook is being set for a thread that is not in the setting application.
  • ERROR_GLOBAL_ONLY_HOOK: A hook that can only be a system hook is being installed to a specific thread.
  • ERROR_INVALID_PARAMETER: The thread ID is invalid.
  • ERROR_JOURNAL_HOOK_SET: There is already a hook set for a journal hook type. Only one journal record or journal playback hook can be installed at one time. This code can also be set if an application tries to set a journal hook while a screen saver is running.
  • ERROR_MOD_NOT_FOUND: The hInstance parameter for a global hook was not a library. (Actually, this value simply means that User was unable to locate the module handle in its list of modules.)
  • Any other value: Security does not allow this hook to be set, or the system is out of memory.

Windows keeps the filter function chain internally (see the figure below) and does not rely on the filter functions to store the address of the next filter function in the chain correctly (as versions of Windows before 3.1 did). Thus, hooks are much more robust in Windows version 3.1 than they were in previous versions. In addition, performance is enhanced significantly because the filter function chain is kept internally.

The filter function chain in Windows 3.1

UnhookWindowsHookEx

To remove a filter function from a hook's chain, call the UnhookWindowsHookEx function. This function takes the hook handle returned from SetWindowsHookEx and returns a value indicating whether the hook was removed. UnhookWindowsHookEx always returns TRUE at this time.

Filter Functions

Filter (hook) functions are functions that are attached to a hook. Because filter functions are called by Windows and not by an application, they are sometimes referred to as callback functions. For consistency, this article uses the term filter functions.

All filter functions must have the following form:

LRESULT CALLBACK FilterFunc( nCode, wParam, lParam )int nCode;
WORD wParam;
DWORD lParam;

All filter functions should return a LONG. FilterFunc is a placeholder for the actual filter function name.

Parameters

Filter functions receive three parameters: ncode (the hook code), wParam, and lParam. The hook code is an integer code that informs the filter function of any additional data it should know. For example, the hook code might indicate what action is causing the hook to be called.

In previous versions of Windows (before 3.1), the hook code indicated whether the filter function should process the event or call DefHookProc. If the hook code is less than zero, the filter function should not process the event; it should call DefHookProc, passing the three parameters it was passed without any modification. Windows used these negative codes to maintain the filter function chain, with help from the applications.

Windows 3.1 still requires that if Windows sends a filter function a negative hook code, the filter function must call CallNextHookEx with the parameters Windows passed to the filter function. The filter function must also return the value returned by CallNextHookEx. Windows 3.1 never sends negative hook codes to filter functions.

The second parameter passed to the filter function, wParam, is a WPARAM, and the third parameter, lParam, is an LPARAM. These parameters pass information needed by the filter function. Each hook attaches different meanings to wParam and lParam. For example, filter functions attached to the WH_KEYBOARD hook receive a virtual-key code in wParam, and lParam contains bit fields that describe the state of the keyboard at the time of the key event. Filter functions attached to the WH_MSGFILTER hook receive a NULL value in wParam and a pointer to a message structure in lParam. Some hooks attach different meanings for wParam and lParam depending on the event that causes the hook to be called. For a complete list of arguments and their meanings for each hook type, see the filter functions listed below in Platform SDK.

Hook Filter function documentation
WH_CALLWNDPROC CallWndProc
WH_CBT CBTProc
WH_DEBUG DebugProc
WH_GETMESSAGE GetMsgProc
WH_JOURNALRECORD JournalRecordProc
WH_JOURNALPLAYBACK JournalPlaybackProc
WH_SHELL ShellProc
WH_KEYBOARD KeyboardProc
WH_MOUSE MouseProc
WH_MSGFILTER MessageProc
WH_SYSMSGFILTER SysMsgProc

Calling the next function in the filter function chain

When a hook is set, Windows calls the first function in the hook's filter function chain, and the responsibility of Windows ends. The filter function is responsible for ensuring that the next filter function in the chain is called. Windows supplies CallNextHookEx to enable a filter function to call the next filter in the filter function chain. CallNextHookEx takes four parameters.

The first parameter is the value returned from the SetWindowsHookEx call. This value is currently ignored by Windows, but this behavior may change in the future.

The next three parameters—nCode, wParam, and lParam—are the parameters that Windows passed to the filter function.

Windows stores the filter function chain internally and keeps track of which filter function it is calling. When CallNextHookEx is called, Windows determines the next filter function in the chain and calls that function.

At times, a filter function may not want to pass an event to the other hook functions on the same chain. In particular, when a hook allows a filter function to discard an event and the filter function decides to do so, the function must not call CallNextHookEx. When a filter function modifies a message, it may decide not to pass the message to the rest of the filter function chain.

Because filter functions are not installed in any particular order, you cannot be sure where your function is in the filter function chain at any moment except at the moment of installation, when it is the first function in the chain. As a result, you are never absolutely certain that you will get every event that occurs. A filter function installed ahead of your filter function in the chain—a function that was installed after your function timewise—might not pass the event to your filter function.

Filter functions in DLLs

Systemwide filter functions must reside in a DLL. In Win16 it was possible (although not recommended) to install a systemwide hook to a filter function in an application. This does not work in Win32. Do not install systemwide filter functions that are not in DLLs, even if it does seem to work on a particular system. The journal hooks, WH_JOURNALRECORD and WH_JOURNALPLAYBACK, are exceptions to this rule. Because of the way Windows calls these hooks, their filter functions do not have to be in a DLL.

Filter functions for systemwide hooks must be prepared to share any data they need across the different processes they are running from. A DLL is mapped into the address space of each of its client processes. Global variables within the DLL will be instance specific unless they are placed in a shared data section. For example, the HOOKSDLL.DLL library in the Hooks sample application needs to share two data items:

  • The window handle to display messages.
  • The height of the text lines in that window.

To share this data, HOOKSDLL puts these data items in a shared data section. Here are the steps HOOKSDLL takes to share the data:

  • Use pragmas to place the data in a named data segment. Note that the data must be initialized for this to work.
    // Shared DATA
    #pragma data_seg(".SHARDATA")
    static HWND   hwndMain = NULL;  // Main hwnd. We will get this from the app.
    static int    nLineHeight = 0;  // Height of lines in window.
    #pragma data_seg()
    
  • Add a SECTIONS statement to the DLL's .DEF file:
    SECTIONS
       .SHARDATA   Read Write Shared
    
  • Create an .EXP file from the .DEF file:
    hooksdll.exp: hooksdll.obj hooksdll.def
        $(implib) -machine:$(CPU)     \
       -def:hooks.def      \
       hooksdll.obj  \
       -out:hooksdll.lib
    
  • Link with the HOOKSDLL.EXP file:
    hooksdll.dll: hooksdll.obj hooksdll.def hooksdll.lib hooksdll.exp
        $(link) $(linkdebug)     \
        -base:0x1C000000  \
        -dll       \
       -entry:LibMain$(DLLENTRY)     \
       -out:hooksdll.dll    \
       hooksdll.exp hooksdll.obj hooksdll.rbj \
       $(guilibsdll)
    

Types of Hooks

WH_CALLWNDPROC

Windows calls this hook whenever the Windows SendMessage function is called. The filter functions receive a hook code from Windows indicating whether the message was sent from the current thread and receive a pointer to a structure containing the actual message.

The CWPSTRUCT structure has the following form:

typedef struct tagCWPSTRUCT {
    LPARAM  lParam;
    WPARAM  wParam;
    DWORD   message;
    HWND    hwnd;
} CWPSTRUCT, *PCWPSTRUCT, NEAR *NPCWPSTRUCT, FAR *LPCWPSTRUCT;

Filters can process the message, but cannot modify the message (this was possible in 16-bit Windows). The message is sent to the Windows function for which it was intended. This hook is a significant drain on system performance, especially when it is installed as a systemwide hook, so use it only as a development or debugging tool.

WH_CBT

To write a CBT application, the developer must coordinate the CBT application with the application for which it is written. Windows supplies the WH_CBT hook to make this possible. Windows passes a hook code to the filter function, indicating which event has occurred and the appropriate data for the event.

A filter function attached to the WH_CBT hook needs to be aware of ten hook codes:

  • HCBT_ACTIVATE
  • HCBT_CREATEWND
  • HCBT_DESTROYWND
  • HCBT_MINMAX
  • HCBT_MOVESIZE
  • HCBT_SYSCOMMAND
  • HCBT_CLICKSKIPPED
  • HCBT_KEYSKIPPED
  • HCBT_SETFOCUS
  • HCBT_QS

HCBT_ACTIVATE

Windows calls the WH_CBT hook with this hook code when any window is about to be activated. In the case of thread-specific hooks, the window must be owned by the thread. If the filter function returns TRUE, the window is not activated.

The wParam parameter contains the handle to the window being activated. The lParam parameter contains a far pointer to CBTACTIVATESTRUCT, which has the following structure:

typedef struct tagCBTACTIVATESTRUCT
{
   BOOL    fMouse;         // TRUE if activation results from a 
                           //  mouse click; otherwise FALSE. 
   HWND    hWndActive;     // Contains the handle to the 
                           //  currently active window. 
} CBTACTIVATESTRUCT, *LPCBTACTIVATESTRUCT;

HCBT_CREATEWND

Windows calls the WH_CBT hook with this hook code when a window is about to be created. In the case of thread-specific hooks, the thread must be creating the window. The WH_CBT hook is called before Windows sends the WM_GETMINMAXINFO, WM_NCCREATE, or WM_CREATE messages to the window. Thus, the filter function can return TRUE and not allow the window to be created.

The wParam parameter contains the handle to the window being created. The lParam parameter contains a pointer to the following structure.

/*
 * HCBT_CREATEWND parameters pointed to by lParam
 */
struct CBT_CREATEWND
{
    struct tagCREATESTRUCT *lpcs;     // The create parameters for the 
                                      //  new window.
    HWND           hwndInsertAfter;   // The window this window will
                                      //  be added after, in Z-order.
} CBT_CREATEWND, *LPCBT_CREATEWND;

A filter function can alter the hwndInsertAfter value or the values in lpcs.

HCBT_DESTROYWND

Windows calls the WH_CBT hook with this hook code when Windows is about to destroy any window. In the case of thread-specific hooks, the thread must own the window. Windows calls the WH_CBT hook before the WM_DESTROY message is sent. If the filter function returns TRUE, the window is not destroyed.

The wParam parameter contains the handle to the window being destroyed. The lParam parameter contains 0L.

HCBT_MINMAX

Windows calls the WH_CBT hook with this hook code when Windows is about to minimize or maximize a window. In the case of thread-specific hooks, the thread must own the window. If the filter function returns TRUE, the action does not occur.

The wParam parameter contains the handle to the window being minimized or maximized. The lParam is any one of the SW_* values defined in WINUSER.H specifying the operation that is taking place.

HCBT_MOVESIZE

Windows calls the WH_CBT hook with this hook code when Windows is about to move or size a window, and the user has just finished selecting the new window position or size. In the case of thread-specific hooks, the thread must own the window. If the filter function returns TRUE, the action does not occur.

The wParam parameter contains the handle to the window being moved or sized. The lParam parameter contains an LPRECT that points to the drag rectangle.

HCBT_SYSCOMMAND

Windows calls the WH_CBT hook with this hook code when Windows processes a system command. In the case of a thread-specific hook, the thread must own the window whose System menu is being used. The WH_CBT hook is called from within DefWindowsProc. If an application does not send the WH_SYSCOMMAND message to DefWindowsProc, this hook is not called. If the filter function returns TRUE, the system command is not processed.

The wParam parameter contains the system command (SC_TASKLIST, SC_HOTKEY, and so on) that is about to be performed. If wParam is SC_HOTKEY, the LOWORD of lParam contains the handle to the window for which the hot key applies. If wParam contains any value other than SC_HOTKEY and if the System menu command is selected with the mouse, the LOWORD of lParam contains the horizontal position of the cursor and the HIWORD of lParam contains the vertical position of the cursor.

The following system commands trigger this hook from within DefWindowProc:

SC_CLOSE Close the window.
SC_HOTKEY Activate the window associated with the application-specified hot key.
SC_HSCROLL Scroll horizontally.
SC_KEYMENU Retrieve a menu through a keystroke.
SC_MAXIMIZE Maximize the window.
SC_MINIMIZE Minimize the window.
SC_MOUSEMENU Retrieve a menu through a mouse click.
SC_MOVE Move the window.
SC_NEXTWINDOW Move to the next window.
SC_PREVWINDOW Move to the previous window.
SC_RESTORE Save the previous coordinates (checkpoint).
SC_SCREENSAVE Execute the screen-save application.
SC_SIZE Size the window.
SC_TASKLIST Execute or activate the Windows Task Manager application.
SC_VSCROLL Scroll vertically.

HCBT_CLICKSKIPPED

Windows calls the WH_CBT hook with this hook code when a mouse event is removed from a thread's input queue and the mouse hook is set. Windows will call a systemwide hook when a mouse event is removed from any input queue and either a systemwide mouse hook or a thread-specific hook for the current thread is installed. This hook code is not generated unless a filter function is attached to the WH_MOUSE hook. Despite its name, HCBT_CLICKSKIPPED is called not only for skipped mouse events but also whenever a mouse event is removed from the system queue. Its main use is to install a WH_JOURNALPLAYBACK hook in response to a mouse event. (See the "WM_QUEUESYNC" section below for more information.)

The wParam parameter contains the message identifier for the mouse message—for example, the WM_LBUTTONDOWN or any WM_?BUTTON* messages. The lParam parameter contains a far pointer to MOUSEHOOKSTRUCT, which has the following structure:

typedef struct tagMOUSEHOOKSTRUCT {
    POINT   pt;             // Location of mouse in screen coordinates
    HWND    hwnd;           // Window that receives this message
    UINT    wHitTestCode;   // The result of hit-testing (HT_*)
    DWORD   dwExtraInfo;    // Extra info associated with the current message 
} MOUSEHOOKSTRUCT, FAR *LPMOUSEHOOKSTRUCT, *PMOUSEHOOKSTRUCT;

HCBT_KEYSKIPPED

Windows calls the WH_CBT hook with this hook code when a keyboard event is removed from the system queue and the keyboard hook is set. Windows will call a systemwide hook when a keyboard event is removed from any input queue and either a systemwide keyboard hook or a thread-specific hook for the current thread is installed. This hook code is not generated unless a filter function is attached to the WH_KEYBOARD hook. Despite its name, HCBT_KEYSKIPPED is called not only for skipped keyboard events but also whenever a keyboard event is removed from the system queue. Its main use is to install a WH_JOURNALPLAYBACK hook in response to a keyboard event. (See the "WM_QUEUESYNC" section below for more information.)

The wParam parameter contains the virtual-key code—the same value as wParam of GetMessage or PeekMessage for WM_KEY* messages. The lParam parameter contains the same value as the lParam parameter of GetMessage or PeekMessage for WM_KEY* messages.

WM_QUEUESYNC

While executing, a CBT application often must react to events in the main application. Keyboard or mouse events usually trigger these events. For example, a user clicks an OK button in a dialog box, after which the CBT application wants to play a series of keystrokes to the main application. The CBT application can use a mouse hook to determine whether the OK button was clicked. Upon determining that it wants to play some keystrokes to the main application, the CBT application must wait until the main application completes the processing of the OK button before beginning to play the new keystrokes. The CBT application would not want to apply the keystrokes to the dialog box.

The CBT application can use the WM_QUEUESYNC message to monitor the main application and determine when an action is completed. The CBT application monitors the main application with a mouse or a keyboard hook and looks for events to which it must respond. By watching the main application with a mouse or a keyboard hook, the CBT application becomes aware of when an event that needs a response begins. The CBT application must wait until the event is completed before responding to it.

To determine when the action is complete, the CBT application takes these steps:

  1. The CBT application waits until it receives the WH_CBT hook with an HCBT_CLICKSKIPPED or an HCBT_KEYSKIPPED hook code from Windows. This happens when the event that is causing the action in the main application is removed from the system queue.
  2. The CBT application installs a WH_JOURNALPLAYBACK hook. The CBT application cannot install this hook until it receives either the HCBT_CLICKSKIPPED or the HCBT_KEYSKIPPED hook code. The WH_JOURNALPLAYBACK hook plays a WM_QUEUESYNC message to the CBT application. When the CBT application receives this message, it can respond to the original event. For example, the CBT application might play some keystrokes to the main application.

HCBT_SETFOCUS

Windows calls the WH_CBT hook with this hook code when Windows is about to set the focus to any window. In the case of thread-specific hooks, the window must belong to the thread. If the filter function returns TRUE, the focus does not change.

The wParam parameter contains the handle to the window that receives the focus. The lParam parameter contains the handle to the window that loses the focus.

HCBT_QS

Windows calls the WH_CBT hook with this hook code when a WM_QUEUESYNC message is removed from the system queue while a window is being resized or moved. The hook is not called at any other time. In the case of thread-specific hooks, the window must belong to the thread.

Both the wParam and lParam parameters contain zero.

WH_DEBUG

Windows calls this hook when Windows is about to call a filter function. Filters cannot modify the values for the hook but can stop Windows from calling the original filter function by returning a nonzero value.

The wParam parameter contains the ID of the hook to be called, for example, WH_MOUSE. The lParam parameter contains a pointer to the following structure:

typedef struct tagDEBUGHOOKINFO
{
    DWORD   idThread;  // The thread ID for the current thread
    LPARAM  reserved;
    LPARAM  lParam;    // The lParam for the target filter function
    WPARAM  wParam;    // The wParam for the target filter function
    int     code;
} DEBUGHOOKINFO, *PDEBUGHOOKINFO, NEAR *NPDEBUGHOOKINFO, FAR* LPDEBUGHOOKINFO;

WH_FOREGROUNDIDLE

Windows calls this hook when there is no user input to process for the current thread. In the case of thread-specific hooks, Windows calls the hook only when that thread is the current thread and there is no input for the thread. This is a notification-only hook; both the wParam and lParam parameters are zero.

WH_GETMESSAGE

Windows calls this hook when the GetMessage or the PeekMessage function is about to return a message. The filter functions receive a pointer to a structure containing the actual message from Windows. The message, including any modifications, is sent to the application that originally called GetMessage or PeekMessage. The lParam parameter contains a pointer to a MSG structure:

typedef struct tagMSG {     /* msg */
    HWND   hwnd;      \\ The window whose Winproc will receive the message
    UINT   message;   \\ The message number
    WPARAM wParam;
    LPARAM lParam;
    DWORD  time;      \\ The time the message was posted
    POINT  pt;        \\ The cursor position in screen coordinates 
                      \\  of the message
} MSG;

WH_HARDWARE

This hook is not currently implemented in Win32.

Journal Hooks

Journal hooks are used to record and playback events. They are available only as systemwide hooks and should, therefore, be used as little as possible. These hooks affect all Windows-based applications; although the desktop allows no other hooks, journal hooks can record and play back events from and to the desktop. Another side-effect of journal hooks is that all system input queues are attached though the thread that installed the hook. This means that all system input must pass through this one point of execution.

Win32 takes special steps to allow a user to cancel a journal hook so that it does not lock the system. Windows will uninstall a record or playback journal hook when the user presses CTRL+ESC, ALT+ESC, or CTRL+ALT+DEL. Windows then notifies the application that had a journal hook installed by posting a WM_CANCELJOURNAL message.

WM_CANCELJOURNAL

This message is posted with a NULL window handle so that it is not dispatched to a window procedure. The best way to catch this message is to install a WH_GETMESSAGE filter function that watches for the message. The Win32 documentation also mentions that an application can catch the WM_CANCELJOURNAL message between a call to GetMessage (or PeekMessage) and DispatchMessage. Although the message can be caught at this point, the application may not be there when the message is sent. For example, if the application is in a dialog box, its main message loop will not be called.

The CTRL+ESC, ALT+ESC, and CTRL+ALT+DEL key combinations are built into the system so the user can always stop a journal hook. It would be nice if every application that uses a journal hook could also supply a way for the user to stop journalling. The suggested way to stop journalling is by using VK_CANCEL (CTRL+BREAK).

WH_JOURNALRECORD

Windows calls this hook when it removes an event from the system queue. Thus, these filters are called for all mouse and keyboard events except for those played back by a journal playback hook. Filters may process the message (that is, record or save the event in memory or on disk or both), but cannot modify or discard the message. Filters for this hook may reside in a DLL or an .EXE file. Only the HC_ACTION hook code is implemented in Win32.

HC_ACTION

Windows calls the WH_JOURNALRECORD hook with this hook code when it takes an event from the system queue. The hook code signals the filter function that this is a normal event. The lParam parameter to the filter function contains a pointer to an EVENTMSG structure. The usual recording procedure is to take all EVENTMSG structures passed to the hook and store them in memory or, if events exceed memory storage capacity, write them to disk.

The EVENTMSG structure is defined in WINDOWS.H and has the following structure:

typedef struct tagEVENTMSG {
    UINT    message;
    UINT    paramL;
    UINT    paramH;
    DWORD    time;
    HWND     hwnd;
} EVENTMSG;

typedef struct tagEVENTMSG *PEVENTMSG, NEAR *NPEVENTMSG, FAR *LPEVENTMSG;

The message element of the EVENTMSG structure is the message ID for the message, the WM_* value. The paramL and paramH values depend on whether the event is a mouse or a keyboard event. If it is a mouse event, the values contain the x and y coordinates of the event. If it is a keyboard event, paramL contains the scan code in the HIBYTE and the virtual-key code in the LOBYTE, and paramH contains the repeat count. Bit 15 of the repeat count specifies whether the event is an extended key. The time element of the EVENTMSG structure contains the system time (when the event occurred), which it obtained from the return value of GetTickCount. The hwnd is the window handle for the event.

The amount of time between events is determined by comparing the time element of an event with the time of subsequent events. This time delta is needed when playing back the recorded events.

WH_JOURNALPLAYBACK

This hook is used to provide mouse and keyboard messages to Windows as if they were inserted in the system queue. This hook is generally used to play back events recorded with the WH_JOURNALRECORD hook, but also provides the best way to send events to another application. Whenever a filter function is attached to this hook, Windows calls the first filter function in the function chain to get events. Windows discards any mouse moves while WH_JOURNALPLAYBACK is installed. All other keyboard and mouse input is queued until the WH_JOURNALPLAYBACK hook has no filter functions attached. Filters for this hook may reside in a DLL or an .EXE file. A filter function attached to this hook needs to be aware of the following hook codes:

  • HC_GETNEXT
  • HC_SKIP

HC_GETNEXT

Windows calls the WH_JOURNALPLAYBACK hook with this hook code when it accesses a thread's input queue. In most cases, Windows makes this call many times for the same message. The lParam parameter to the filter function contains a pointer to an EVENTMSG structure (see above). The filter function must put the message, the paramL value, and the paramH value into the EVENTMSG structure. These are usually copied directly from the recorded event made during WH_JOURNALRECORD.

The filter function must tell Windows when to process the message that the filter function is giving Windows. Windows needs two values for its scheduling: (1) the amount of time Windows should wait before processing the message; (2) the time at which the message is to be processed. The usual method of calculating the time to wait before processing is to subtract the EVENTMSG time element of the previous message from the EVENTMSG time element of the current message. This technique plays back messages at the speed at which they were recorded. If the message is to be processed immediately for much faster playback, the amount of time returned from the function is zero.

The time at which the message should be processed is usually obtained by adding the amount of time Windows should wait before processing the message to the current system time obtained from GetTickCount. For immediate playback, use the value returned from GetTickCount.

If the system is not otherwise active, Windows uses the values that the filter function has supplied to process the event. If the system is active, Windows examines the system queue. Each time it does, it asks for the same event with an HC_GETNEXT hook code. Each time the filter function receives HC_GETNEXT, it should return the new amount of time to wait, assuming that time elapsed between calls. The time element of the EVENTMSG structure and of the message, the paramH value, and the paramL value will probably not need changing between calls.

HC_SKIP

Windows calls the WH_JOURNALPLAYBACK hook with this hook code when it has completed processing a message it received from WH_JOURNALPLAYBACK. This occurs at the time that Windows would have removed the event from the system queue, if the event had been in the system queue instead of being generated by a WH_JOURNALPLAYBACK hook. This hook code signals to the filter function that the event that the filter function returned on the prior HC_GETNEXT call has been returned to an application. The filter function should prepare to return the next event on the next HC_GETEVENT call. When the filter function determines that it has no more events to play back, it should unhook itself during this HC_SKIP call.

WH_KEYBOARD

Windows calls this hook when the GetMessage or the PeekMessage function is about to return a WM_KEYUP, WM_KEYDOWN, WM_SYSKEYUP, WM_SYSKEYDOWN, or WM_CHAR message. In the case of thread-specific hooks, the message must be from the thread's input queue. The filter function receives the virtual-key code and the state of the keyboard at the time of the keyboard hook. Filters can tell Windows to discard the message. A filter function attached to this hook needs to be aware of the following two hook codes:

  • HC_ACTION
  • HC_NOREMOVE

HC_ACTION

Windows calls the WH_KEYBOARD hook with this hook code when an event is being removed from the system queue.

HC_NOREMOVE

Windows calls the WH_KEYBOARD hook with this hook code when there is a keyboard event that is not being removed because an application is calling PeekMessage with the PM_NOREMOVE option. If this hook code is passed, the key-state table will not accurately reflect the previous key state. An application needs to be aware of the existence of this condition.

WH_MOUSE

Windows calls this hook when a GetMessage or a PeekMessage function is called and Windows has a mouse message to process. Like the WH_KEYBOARD hook, this filter function receives a hook code, which indicates whether the message is being removed (HC_NOREMOVE), an identifier specifying the mouse message, and the x and y coordinates of the mouse. Filters can tell Windows to discard the message. Filters for this hook must reside in a DLL.

WH_MSGFILTER

Windows calls this hook when a dialog box, a message box, a scroll bar, or a menu retrieves a message, or when the user presses the ALT+TAB or ALT+ESC keys while the application that set the hook is active. This hook is thread specific, so it is always safe for its filter functions to reside in an application or in a DLL. The filter receives the following hook codes:

  • MSGF_DIALOGBOX: The message is for a dialog box or a message box.
  • MSGF_MENU: The message is for a menu.
  • MSGF_SCROLLBAR: The message is for a scroll bar.
  • MSGF_NEXTWINDOW: The next window action is about to take place.

There are other MSGF_ values defined in WINUSER.H but they are not used in WH_MSGFILTER hooks at this time.

The lParam parameter contains a pointer to a structure containing the message. The WH_SYSMSGFILTER hooks are called before the WH_MSGFILTER hooks. If any of the WH_SYSMSGFILTER hook functions return TRUE, the WH_MSGFILTER hooks are not called.

WH_SHELL

Windows calls this hook when actions occur to top-level (that is, unowned) windows. In the case of thread-specific hooks, Windows calls this hook only for windows that belong to the thread. This is a notification-only hook, so the filter function cannot modify or discard the event. The wParam parameter contains the handle to the window; the lParam parameter is not used. Three hook codes are defined in WINUSER.H for this hook:

  • HSHELL_WINDOWCREATED: Windows calls the WH_SHELL hook when a top-level window is created. The window already exists when this hook is called.
  • HSHELL_WINDOWDESTROYED: Windows calls the WH_SHELL hook when a top-level window is about to be destroyed.
  • HSHELL_ACTIVATESHELLWINDOW: This hook code is not used at this time.

WH_SYSMSGFILTER

This hook is identical to WH_MSGFILTER except that it is a systemwide hook. Windows calls this hook when a dialog box, a message box, a scroll bar, or a menu retrieves a message, or when the user presses the ALT+TAB or ALT+ESC keys. The filter receives the same hook code as WH_MSGFILTER.

The lParam parameter contains a pointer to a structure containing the message. The WH_SYSMSGFILTER hooks are called before the WH_MSGFILTER hooks. If any of the WH_SYSMSGFILTER hook functions return TRUE, the WH_MSGFILTER hooks are not called.



MSDN Library
Win32 and COM Development
Technical Articles
User Interface
User Interface Design and Usability
Win32 Hooks