Search
Module:
Directory

   Desktop Functions:

   Smart Device Functions:


Show Recent Changes
Subscribe (RSS)
Misc. Pages
Comments
FAQ
Helpful Tools
Playground
Suggested Reading
Website TODO List
Download Visual Studio Add-In

SetWindowsHookEx (user32)
 
.
Summary
Installs a hook to monitor certain types of events.

C# Signature:

/// <summary>
///     Installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the
///     system for certain types of events. These events are associated either with a specific thread or with all threads
///     in the same desktop as the calling thread.
///     <para>See https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990%28v=vs.85%29.aspx for more information</para>
/// </summary>
/// <param name="hookType">
///     C++ ( idHook [in]. Type: int )<br />The type of hook procedure to be installed. This parameter can be one of the
///     following values.
///     <list type="table">
///     <listheader>
///         <term>Possible Hook Types</term>
///     </listheader>
///     <item>
///         <term>WH_CALLWNDPROC (4)</term>
///         <description>
///         Installs a hook procedure that monitors messages before the system sends them to the
///         destination window procedure. For more information, see the CallWndProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_CALLWNDPROCRET (12)</term>
///         <description>
///         Installs a hook procedure that monitors messages after they have been processed by the
///         destination window procedure. For more information, see the CallWndRetProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_CBT (5)</term>
///         <description>
///         Installs a hook procedure that receives notifications useful to a CBT application. For more
///         information, see the CBTProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_DEBUG (9)</term>
///         <description>
///         Installs a hook procedure useful for debugging other hook procedures. For more information,
///         see the DebugProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_FOREGROUNDIDLE (11)</term>
///         <description>
///         Installs a hook procedure that will be called when the application's foreground thread is
///         about to become idle. This hook is useful for performing low priority tasks during idle time. For more
///         information, see the ForegroundIdleProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_GETMESSAGE (3)</term>
///         <description>
///         Installs a hook procedure that monitors messages posted to a message queue. For more
///         information, see the GetMsgProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_JOURNALPLAYBACK (1)</term>
///         <description>
///         Installs a hook procedure that posts messages previously recorded by a WH_JOURNALRECORD hook
///         procedure. For more information, see the JournalPlaybackProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_JOURNALRECORD (0)</term>
///         <description>
///         Installs a hook procedure that records input messages posted to the system message queue. This
///         hook is useful for recording macros. For more information, see the JournalRecordProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_KEYBOARD (2)</term>
///         <description>
///         Installs a hook procedure that monitors keystroke messages. For more information, see the
///         KeyboardProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_KEYBOARD_LL (13)</term>
///         <description>
///         Installs a hook procedure that monitors low-level keyboard input events. For more information,
///         see the LowLevelKeyboardProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_MOUSE (7)</term>
///         <description>
///         Installs a hook procedure that monitors mouse messages. For more information, see the
///         MouseProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_MOUSE_LL (14)</term>
///         <description>
///         Installs a hook procedure that monitors low-level mouse input events. For more information,
///         see the LowLevelMouseProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_MSGFILTER (-1)</term>
///         <description>
///         Installs a hook procedure that monitors messages generated as a result of an input event in a
///         dialog box, message box, menu, or scroll bar. For more information, see the MessageProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_SHELL (10)</term>
///         <description>
///         Installs a hook procedure that receives notifications useful to shell applications. For more
///         information, see the ShellProc hook procedure.
///         </description>
///     </item>
///     <item>
///         <term>WH_SYSMSGFILTER (6)</term><description></description>
///     </item>
///     </list>
/// </param>
/// <param name="lpfn">
///     C++ ( lpfn [in]. Type: HOOKPROC )<br />A pointer to the hook procedure. If the dwThreadId parameter
///     is zero or specifies the identifier of a thread created by a different process, the lpfn parameter must point to a
///     hook procedure in a DLL. Otherwise, lpfn can point to a hook procedure in the code associated with the current
///     process.
/// </param>
/// <param name="hMod">
///     C++ ( hMod [in]. Type: HINSTANCE )<br />A handle to the DLL containing the hook procedure pointed to
///     by the lpfn parameter. The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread
///     created by the current process and if the hook procedure is within the code associated with the current process.
/// </param>
/// <param name="dwThreadId">
///     C++ ( dwThreadId [in]. Type: DWORD )<br />The identifier of the thread with which the hook
///     procedure is to be associated. For desktop apps, if this parameter is zero, the hook procedure is associated with
///     all existing threads running in the same desktop as the calling thread. For Windows Store apps, see the Remarks
///     section.
/// </param>
/// <returns>
///     C++ ( Type: HHOOK )<br />If the function succeeds, the return value is the handle to the hook procedure. If
///     the function fails, the return value is NULL.
///     <para>To get extended error information, call GetLastError.</para>
/// </returns>
/// <remarks>
///     <para>
///     SetWindowsHookEx can be used to inject a DLL into another process. A 32-bit DLL cannot be injected into a
///     64-bit process, and a 64-bit DLL cannot be injected into a 32-bit process. If an application requires the use
///     of hooks in other processes, it is required that a 32-bit application call SetWindowsHookEx to inject a 32-bit
///     DLL into 32-bit processes, and a 64-bit application call SetWindowsHookEx to inject a 64-bit DLL into 64-bit
///     processes. The 32-bit and 64-bit DLLs must have different names.
///     </para>
///     <para>
///     Because hooks run in the context of an application, they must match the "bitness" of the application. If a
///     32-bit application installs a global hook on 64-bit Windows, the 32-bit hook is injected into each 32-bit
///     process (the usual security boundaries apply). In a 64-bit process, the threads are still marked as "hooked."
///     However, because a 32-bit application must run the hook code, the system executes the hook in the hooking app's
///     context; specifically, on the thread that called SetWindowsHookEx. This means that the hooking application must
///     continue to pump messages or it might block the normal functioning of the 64-bit processes.
///     </para>
///     <para>
///     If a 64-bit application installs a global hook on 64-bit Windows, the 64-bit hook is injected into each
///     64-bit process, while all 32-bit processes use a callback to the hooking application.
///     </para>
///     <para>
///     To hook all applications on the desktop of a 64-bit Windows installation, install a 32-bit global hook and a
///     64-bit global hook, each from appropriate processes, and be sure to keep pumping messages in the hooking
///     application to avoid blocking normal functioning. If you already have a 32-bit global hooking application and
///     it doesn't need to run in each application's context, you may not need to create a 64-bit version.
///     </para>
///     <para>
///     An error may occur if the hMod parameter is NULL and the dwThreadId parameter is zero or specifies the
///     identifier of a thread created by another process.
///     </para>
///     <para>
///     Calling the CallNextHookEx function to chain to the next hook procedure is optional, but it is highly
///     recommended; otherwise, other applications that have installed hooks will not receive hook notifications and
///     may behave incorrectly as a result. You should call CallNextHookEx unless you absolutely need to prevent the
///     notification from being seen by other applications.
///     </para>
///     <para>
///     Before terminating, an application must call the UnhookWindowsHookEx function to free system resources
///     associated with the hook.
///     </para>
///     <para>
///     The scope of a hook depends on the hook type. Some hooks can be set only with global scope; others can also
///     be set for only a specific thread, as shown in the following table.
///     </para>
///     <list type="table">
///     <listheader>
///         <term>Possible Hook Types</term>
///     </listheader>
///     <item>
///         <term>WH_CALLWNDPROC (4)</term>
///         <description>Thread or global</description>
///     </item>
///     <item>
///         <term>WH_CALLWNDPROCRET (12)</term>
///         <description>Thread or global</description>
///     </item>
///     <item>
///         <term>WH_CBT (5)</term>
///         <description>Thread or global</description>
///     </item>
///     <item>
///         <term>WH_DEBUG (9)</term>
///         <description>Thread or global</description>
///     </item>
///     <item>
///         <term>WH_FOREGROUNDIDLE (11)</term>
///         <description>Thread or global</description>
///     </item>
///     <item>
///         <term>WH_GETMESSAGE (3)</term>
///         <description>Thread or global</description>
///     </item>
///     <item>
///         <term>WH_JOURNALPLAYBACK (1)</term>
///         <description>Global only</description>
///     </item>
///     <item>
///         <term>WH_JOURNALRECORD (0)</term>
///         <description>Global only</description>
///     </item>
///     <item>
///         <term>WH_KEYBOARD (2)</term>
///         <description>Thread or global</description>
///     </item>
///     <item>
///         <term>WH_KEYBOARD_LL (13)</term>
///         <description>Global only</description>
///     </item>
///     <item>
///         <term>WH_MOUSE (7)</term>
///         <description>Thread or global</description>
///     </item>
///     <item>
///         <term>WH_MOUSE_LL (14)</term>
///         <description>Global only</description>
///     </item>
///     <item>
///         <term>WH_MSGFILTER (-1)</term>
///         <description>Thread or global</description>
///     </item>
///     <item>
///         <term>WH_SHELL (10)</term>
///         <description>Thread or global</description>
///     </item>
///     <item>
///         <term>WH_SYSMSGFILTER (6)</term>
///         <description>Global only</description>
///     </item>
///     </list>
///     <para>
///     For a specified hook type, thread hooks are called first, then global hooks. Be aware that the WH_MOUSE,
///     WH_KEYBOARD, WH_JOURNAL*, WH_SHELL, and low-level hooks can be called on the thread that installed the hook
///     rather than the thread processing the hook. For these hooks, it is possible that both the 32-bit and 64-bit
///     hooks will be called if a 32-bit hook is ahead of a 64-bit hook in the hook chain.
///     </para>
///     <para>
///     The global hooks are a shared resource, and installing one affects all applications in the same desktop as
///     the calling thread. All global hook functions must be in libraries. Global hooks should be restricted to
///     special-purpose applications or to use as a development aid during application debugging. Libraries that no
///     longer need a hook should remove its hook procedure.
///     </para>
///     <para>
///     Windows Store app development If dwThreadId is zero, then window hook DLLs are not loaded in-process for the
///     Windows Store app processes and the Windows Runtime broker process unless they are installed by either UIAccess
///     processes (accessibility tools). The notification is delivered on the installer's thread for these hooks:
///     </para>
///     <list type="bullet">
///     <item>
///         <term>WH_JOURNALPLAYBACK</term>
///     </item>
///     <item>
///         <term>WH_JOURNALRECORD </term>
///     </item>
///     <item>
///         <term>WH_KEYBOARD </term>
///     </item>
///     <item>
///         <term>WH_KEYBOARD_LL </term>
///     </item>
///     <item>
///         <term>WH_MOUSE </term>
///     </item>
///     <item>
///         <term>WH_MOUSE_LL </term>
///     </item>
///     </list>
///     <para>
///     This behavior is similar to what happens when there is an architecture mismatch between the hook DLL and the
///     target application process, for example, when the hook DLL is 32-bit and the application process 64-bit.
///     </para>
///     <para>
///     For an example, see Installing and
///     <see
///         cref="!:https://msdn.microsoft.com/en-us/library/windows/desktop/ms644960%28v=vs.85%29.aspx#installing_releasing">
///         Releasing
///         Hook Procedures.
///     </see>
///     [
///     https://msdn.microsoft.com/en-us/library/windows/desktop/ms644960%28v=vs.85%29.aspx#installing_releasing ]
///     </para>
/// </remarks>
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, uint dwThreadId);

VB.NET Signature:

<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function SetWindowsHookEx(ByVal hookType As HookType, ByVal lpfn As HookProc, ByVal hMod As IntPtr, ByVal dwThreadId As UInteger) As IntPtr
End Function

User-Defined Types:

A HookType constant specifying the type of hook to install. Used as the value passed into idHook.

A HookProc delegate representing the hook procedure method. Used as the value passed into lpfn (long pointer to function).

Notes:

This will enable you to install application hooks. However, you cannot implement global hooks in Microsoft .NET Framework except low level hooks. To install a global hook, a hook must have a native dynamic-link library (DLL) export to inject itself in another process that requires a valid, consistent function to call into. This requires a DLL export, which .NET Framework does not support. Managed code has no concept of a consistent value for a function pointer because these function pointers are proxies that are built dynamically.

AppDomain.GetCurrentThreadId is marked as deprecated in favour of Thread.ManagedThreadId but this will not to work with unmanaged API.

You can use the GetCurrentThreadId API as well:

To hook low level events use:

IntPtr hHook;

using (Process process = Process.GetCurrentProcess())
using (ProcessModule module = process.MainModule)
{
    IntPtr hModule = GetModuleHandle(module.ModuleName);

    hHook = SetWindowsHookEx(HookType.WH_KEYBOARD_LL, hook, hModule, 0);
}

Tips & Tricks:

Remember to keep the HookProc delegate alive manually, such as using a class member as shown in the example below, otherwise the garbage collector will clean up your hook delegate eventually, resulting in your code throwing a System.NullReferenceException.

Remember to call CallNextHookEx in your callback delegate.

Remember to call UnhookWindowsHookEx using the handle returned by SetWindowsHookEx

Note how you can import the same function several times with different overloaded signatures to handle the common case where one parameter is an opaque pointer pointing to a struct that depends on another parameter.

Sample Code:

// this sample installs a keyboard hook

using System.Windows.Forms;
public class MyClass
{
     private HookProc myCallbackDelegate = null;

     public MyClass()
     {
     // initialize our delegate
     this.myCallbackDelegate = new HookProc(this.MyCallbackFunction);

     // setup a keyboard hook
     SetWindowsHookEx(HookType.WH_KEYBOARD, this.myCallbackDelegate, IntPtr.Zero, AppDomain.GetCurrentThreadId());
     }

     [DllImport("user32.dll")]
     protected static extern IntPtr SetWindowsHookEx(HookType code, HookProc func, IntPtr hInstance, int threadID);

     [DllImport("user32.dll")]
     static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

     private int MyCallbackFunction(int code, IntPtr wParam, IntPtr lParam)
     {
    if (code < 0) {
        //you need to call CallNextHookEx without further processing
        //and return the value returned by CallNextHookEx
        return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
    }
     // we can convert the 2nd parameter (the key code) to a System.Windows.Forms.Keys enum constant
     Keys keyPressed = (Keys)wParam.ToInt32();
     Console.WriteLine(keyPressed);
    //return the value returned by CallNextHookEx
    return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
     }
}

VB.NET:

' My sample installs a keyboard hook
Imports System.Windows.Forms
Imports System.Runtime.InteropServices

Public Class MyClass1
    Delegate Function HookProc(ByVal code As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer

    Private myCallbackDelegate As HookProc = Nothing

    Public Sub New()
    ' initialize our delegate
    Me.myCallbackDelegate = New HookProc(AddressOf Me.MyCallbackFunction)

    ' setup a keyboard hook
    SetWindowsHookEx(HookType.WH_KEYBOARD, Me.myCallbackDelegate, IntPtr.Zero, AppDomain.GetCurrentThreadId())
    End Sub

    <DllImport("user32.dll")> _
    Friend Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal lpfn As HookProc, ByVal hInstance As IntPtr, ByVal threadId As Integer) As Integer
    End Function

    <DllImport("user32.dll")> _
    Friend Shared Function CallNextHookEx(ByVal hhk As intptr, ByVal nCode As Integer, ByVal wParam As intptr, ByVal lParam As intptr) As Integer
    End Function

    Private Function MyCallbackFunction(ByVal code As Integer, ByVal wParam As intptr, ByVal lParam As intptr) As Integer
    If (code < 0) Then
        'you need to call CallNextHookEx without further processing
        'and return the value returned by CallNextHookEx
        Return CallNextHookEx(IntPtr.Zero, code, wParam, lParam)    'unt
    End If
    ' we can convert the 2nd parameter (the key code) to a System.Windows.Forms.Keys enum constant
    Dim keyPressed As Keys = CType(wParam.ToInt32, Keys)
    Console.WriteLine(keyPressed)
    'return the value returned by CallNextHookEx
    Return CallNextHookEx(IntPtr.Zero, code, wParam, lParam)
    End Function
End Class

Alternative Managed API:

The ManagedWindowsApi project (http://mwinapi.sourceforge.net) provides a Hook class and subclasses for Journal hooks, Message hooks and Low-Level hooks.

Documentation

SetWindowsHookEx on MSDN

MSDN example of a mouse hook in C#: http://support.microsoft.com/default.aspx?scid=kb;en-us;318804#3

MSDN article on hooks in .NET: http://msdn.microsoft.com/msdnmag/issues/02/10/CuttingEdge/

MSDN article on KeyboardProc: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/keyboardproc.asp

Stephen Toub entry on low-level keyboard hook in C#: http://blogs.msdn.com/toub/archive/2006/05/03/589423.aspx

Stephen Toub entry on low-level mouse hook on C#: http://blogs.msdn.com/toub/archive/2006/05/03/589468.aspx

.

Please edit this page!

Do you have...

  • helpful tips or sample code to share for using this API in managed code?
  • corrections to the existing content?
  • variations of the signature you want to share?
  • additional languages you want to include?

Select "Edit This Page" on the right hand toolbar and edit it! Or add new pages containing supporting types needed for this API (structures, delegates, and more).

 
Access PInvoke.net directly from VS:
Terms of Use
Edit This Page
Find References
Show Printable Version
Revisions