C# hook captures all keyboard and mouse events and can be used to: judge when there is no mouse or keyboard operation and close the Winform form
If there is no operation for 5 minutes, the Form will be automatically closed.
The function of hooks is mainly to monitor and intercept various event messages in the system or process, and to perform customized processing. Hooks can intercept and process corresponding messages. For example, keyboard hooks can intercept keyboard messages, and shell hooks can intercept, start and close application messages, etc. Hooks are divided into thread hooks and system hooks. Thread hooks monitor event messages of specified threads, while system hooks monitor event messages of all threads in the system.
Specific application scenarios and functions of hooks
- Keyboard and mouse input monitoring: Hooks can intercept keyboard and mouse input for recording user operations or conducting automated testing.
- Screen capture and log monitoring: By monitoring system events, hooks can implement the screen capture function or record system operation logs.
- Application monitoring: Shell hooks can intercept, start and close application messages, and are used to monitor or manage application behavior.
Types of hooks and their functions
- Keyboard hook: Intercept keyboard messages and use them to record keyboard input or perform input interception.
- Mouse hook: intercepts mouse events and is used to monitor mouse operations.
- Shell hooks: Intercept, start and close application messages, used to manage application behavior.
- Log hook: monitors system log events and records system operation logs.
How hooks work
A hook is a monitoring point in the Windows message processing mechanism. An application can install a monitoring subroutine here to monitor the system's message flow before it reaches the destination window. In this way, hooks can implement various customized functions and processing logic
-
GlobalHook
-
KeyboardHook
-
MouseHook
-
KeyboardSimulator
-
MouseSimulator
GlobalHook
using System;
using ;
using ;
using ;
using ;
namespace
{
///
/// Abstract base class for Mouse and Keyboard hooks
///
public abstract class GlobalHook
{
#region Windows API Code
///
/// Used to control the layout of fields of a structure or class in memory
/// Specifically, means that the fields will be arranged in the order they are declared in the source code
/// The structure fields will be arranged in memory in the order of x and y
/// - Sequential: Fields are arranged in declaration order, no padding bytes are inserted.
/// - Explicit: allows developers to precisely control the storage location of each field, using the FieldOffset property to specify the offset.
/// - Auto: The compiler chooses the optimal layout, which is generally not recommended for scenarios where you interact with unmanaged code.
///
[StructLayout()]
protected class POINT
{
public int x;
public int y;
}
[StructLayout()]
protected class MouseHookStruct
{
public POINT pt;
public int hwnd;
public int wHitTestCode;
public int dwExtraInfo;
}
[StructLayout()]
protected class MouseLLHookStruct
{
public POINT pt;
public int mouseData;
public int flags;
public int time;
public int dwExtraInfo;
}
[StructLayout()]
protected class KeyboardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
///
/// The CallingConvention attribute specifies the calling convention, which defines how the function receives parameters and returns values. Common calling conventions include:
/// -: The caller clears the stack, mostly used in C/C++ libraries.
/// -: The callee clears the stack, commonly used in Windows API.
/// -: used for C++ class methods.
/// -: Used for quick calls, rarely used.
/// The CharSet attribute is used to specify the character set of the string, which affects how the string is processed and transmitted. The main options are:
/// -: Pass the string as ANSI encoding.
/// -: Pass the string as Unicode encoding.
/// -: Automatically select ANSI or Unicode depending on the platform.
/// The SetLastError property specifies whether GetLastError is called after calling an unmanaged function. When set to true, the error code can be obtained using Marshal.GetLastWin32Error.
///
/// idHook, indicates the type of hook process to be installed
/// lpfn, points to the corresponding hook processing process. If the parameter dwThreadId is 0 or indicates the identifier of a thread created by another process, the parameter lpfn must point to a hook processing process in a dynamic link. Otherwise, the parameter lpfn can point to A hook handler defined in code related to the current process
/// hMod indicates a dynamic link handle. The dynamic link library contains the hook processing process pointed to by the parameter lpfn. If the thread indicated by the parameter dwThreadId is created by the current process, and the corresponding hook processing process is defined in the current process-related code, the parameter hMod must be set to NULL(0).
/// dwThreadId indicates a thread identifier, and the hook processing process is related to the thread. If the value of this parameter is 0, the hook processing process is related to all existing threads.
///
///
[DllImport("", CharSet = , CallingConvention = , SetLastError = true)]
protected static extern int SetWindowsHookEx(
int idHook,
HookProc lpfn,
IntPtr hMod,
int dwThreadId);
[DllImport("", CharSet = , CallingConvention = , SetLastError = true)]
protected static extern int UnhookWindowsHookEx(int idHook);
[DllImport("", CharSet = ,CallingConvention = )]
protected static extern int CallNextHookEx(
int idHook,
int nCode,
int wParam,
IntPtr lParam);
[DllImport("user32")]
protected static extern int ToAscii(
int uVirtKey,
int uScanCode,
byte[] lpbKeyState,
byte[] lpwTransKey,
int fuState);
[DllImport("user32")]
protected static extern int GetKeyboardState(byte[] pbKeyState);
[DllImport("", CharSet = , CallingConvention = )]
protected static extern short GetKeyState(int vKey);
protected delegate int HookProc(int nCode, int wParam, IntPtr lParam);
protected const int WH_MOUSE_LL = 14; //This hook can only be installed in Windows NT and is used to monitor underlying mouse input events. For details, see the LowLevelMouseProc hook processing process.
protected const int WH_KEYBOARD_LL = 13; //This hook can only be installed in Windows NT and is used to monitor underlying keyboard input events. For details, see the LowLevelKeyboardProc hook processing process.
protected const int WH_MOUSE = 7; //Install a hook processing process to monitor mouse messages. For details, see the MouseProc hook processing process.
protected const int WH_KEYBOARD = 2; //Install a hook processing process to monitor keystroke messages. For details, see the KeyboardProc hook processing process.
protected const int WM_MOUSEMOVE = 0x200; //mouse movement
protected const int WM_LBUTTONDOWN = 0x201; //Mouse left button pressed
protected const int WM_RBUTTONDOWN = 0x204; //right mouse button pressed
protected const int WM_MBUTTONDOWN = 0x207; //Mouse wheel pressed
protected const int WM_LBUTTONUP = 0x202;
protected const int WM_RBUTTONUP = 0x205;
protected const int WM_MBUTTONUP = 0x208;
protected const int WM_LBUTTONDBLCLK = 0x203;
protected const int WM_RBUTTONDBLCLK = 0x206;
protected const int WM_MBUTTONDBLCLK = 0x209;
protected const int WM_MOUSEWHEEL = 0x020A;
protected const int WM_KEYDOWN = 0x100;
protected const int WM_KEYUP = 0x101;
protected const int WM_SYSKEYDOWN = 0x104;
protected const int WM_SYSKEYUP = 0x105;
protected const byte VK_SHIFT = 0x10;
protected const byte VK_CAPITAL = 0x14;
protected const byte VK_NUMLOCK = 0x90;
protected const byte VK_LSHIFT = 0xA0;
protected const byte VK_RSHIFT = 0xA1;
protected const byte VK_LCONTROL = 0xA2;
protected const byte VK_RCONTROL = 0x3;
protected const byte VK_LALT = 0xA4;
protected const byte VK_RALT = 0xA5;
protected const byte LLKHF_ALTDOWN = 0x20;
#endregion
#region Private Variables
protected int _hookType;
protected int _handleToHook;
protected bool _isStarted;
protected HookProc _hookCallback;
#endregion
#region Properties
public bool IsStarted
{
get
{
return _isStarted;
}
}
#endregion
#region Constructor
public GlobalHook()
{
+= new EventHandler(Application_ApplicationExit);
}
#endregion
#regionMethods
///
/// Enable hook
///
public void Start()
{
if (!_isStarted &&
_hookType != 0)
{
// Make sure we keep a reference to this delegate!
// If not, GC randomly collects it, and a NullReference exception is thrown
_hookCallback = new HookProc(HookCallbackProcedure);
_handleToHook = SetWindowsHookEx(
_hookType,
_hookCallback,
(().GetModules()[0]),
0);
// Were we able to successfully start hook?
if (_handleToHook != 0)
{
_isStarted = true;
}
}
}
///
/// Stop hook
///
public void Stop()
{
if (_isStarted)
{
UnhookWindowsHookEx(_handleToHook);
_isStarted = false;
}
}
protected virtual int HookCallbackProcedure(int nCode, Int32 wParam, IntPtr lParam)
{
// This method must be overriden by each extending hook
return 0;
}
protected void Application_ApplicationExit(object sender, EventArgs e)
{
if (_isStarted)
{
Stop();
}
}
#endregion
}
}
KeyboardHook
using System;
using ;
using ;
using ;
namespace
{
/// <summary>
/// Captures global keyboard events
/// </summary>
public class KeyboardHook : GlobalHook
{
#region Events
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
public event KeyPressEventHandler KeyPress;
#endregion
#region Constructor
public KeyboardHook()
{
_hookType = WH_KEYBOARD_LL;
}
#endregion
#region Methods
protected override int HookCallbackProcedure(int nCode, int wParam, IntPtr lParam)
{
bool handled = false;
if (nCode > -1 && (KeyDown != null || KeyUp != null || KeyPress != null))
{
KeyboardHookStruct keyboardHookStruct =
(KeyboardHookStruct)(lParam, typeof(KeyboardHookStruct));
// Is Control being held down?
bool control = ((GetKeyState(VK_LCONTROL) & 0x80) != 0) ||
((GetKeyState(VK_RCONTROL) & 0x80) != 0);
// Is Shift being held down?
bool shift = ((GetKeyState(VK_LSHIFT) & 0x80) != 0) ||
((GetKeyState(VK_RSHIFT) & 0x80) != 0);
// Is Alt being held down?
bool alt = ((GetKeyState(VK_LALT) & 0x80) != 0) ||
((GetKeyState(VK_RALT) & 0x80) != 0);
// Is CapsLock on?
bool capslock = (GetKeyState(VK_CAPITAL) != 0);
// Create event using keycode and control/shift/alt values found above
KeyEventArgs e = new KeyEventArgs(
(Keys)(
|
(control ? (int) : 0) |
(shift ? (int) : 0) |
(alt ? (int) : 0)
));
// Handle KeyDown and KeyUp events
switch (wParam)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
if (KeyDown != null)
{
KeyDown(this, e);
handled = handled || ;
}
break;
case WM_KEYUP:
case WM_SYSKEYUP:
if (KeyUp != null)
{
KeyUp(this, e);
handled = handled || ;
}
break;
}
// Handle KeyPress event
if (wParam == WM_KEYDOWN &&
!handled &&
! &&
KeyPress != null)
{
byte[] keyState = new byte[256];
byte[] inBuffer = new byte[2];
GetKeyboardState(keyState);
if (ToAscii(,
,
keyState,
inBuffer,
) == 1)
{
char key = (char)inBuffer[0];
if ((capslock ^ shift) && (key))
key = (key);
KeyPressEventArgs e2 = new KeyPressEventArgs(key);
KeyPress(this, e2);
handled = handled || ;
}
}
}
if (handled)
{
return 1;
}
else
{
return CallNextHookEx(_handleToHook, nCode, wParam, lParam);
}
}
#endregion
}
}
MouseHook
using System;
using ;
using ;
using ;
namespace
{
/// <summary>
/// Captures global mouse events
/// </summary>
public class MouseHook : GlobalHook
{
#region MouseEventType Enum
private enum MouseEventType
{
None,
MouseDown,
MouseUp,
DoubleClick,
MouseWheel,
MouseMove
}
#endregion
#region Events
public event MouseEventHandler MouseDown;
public event MouseEventHandler MouseUp;
public event MouseEventHandler MouseMove;
public event MouseEventHandler MouseWheel;
public event EventHandler Click;
public event EventHandler DoubleClick;
#endregion
#region Constructor
public MouseHook()
{
_hookType = WH_MOUSE_LL;
}
#endregion
#region Methods
protected override int HookCallbackProcedure(int nCode, int wParam, IntPtr lParam)
{
if (nCode > -1 && (MouseDown != null || MouseUp != null || MouseMove != null))
{
MouseLLHookStruct mouseHookStruct =
(MouseLLHookStruct)(lParam, typeof(MouseLLHookStruct));
MouseButtons button = GetButton(wParam);
MouseEventType eventType = GetEventType(wParam);
MouseEventArgs e = new MouseEventArgs(
button,
(eventType == ? 2 : 1),
,
,
(eventType == ? (short)(( >> 16) & 0xffff) : 0));
// Prevent multiple Right Click events (this probably happens for popup menus)
if (button == && != 0)
{
eventType = ;
}
switch (eventType)
{
case :
if (MouseDown != null)
{
MouseDown(this, e);
}
break;
case :
if (Click != null)
{
Click(this, new EventArgs());
}
if (MouseUp != null)
{
MouseUp(this, e);
}
break;
case :
if (DoubleClick != null)
{
DoubleClick(this, new EventArgs());
}
break;
case :
if (MouseWheel != null)
{
MouseWheel(this, e);
}
break;
case :
if (MouseMove != null)
{
MouseMove(this, e);
}
break;
default:
break;
}
}
return CallNextHookEx(_handleToHook, nCode, wParam, lParam);
}
private MouseButtons GetButton(Int32 wParam)
{
switch (wParam)
{
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
return ;
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDBLCLK:
return ;
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
return ;
default:
return ;
}
}
private MouseEventType GetEventType(Int32 wParam)
{
switch (wParam)
{
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
return ;
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
return ;
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
return ;
case WM_MOUSEWHEEL:
return ;
case WM_MOUSEMOVE:
return ;
default:
return ;
}
}
#endregion
}
}
KeyboardSimulator
using System;
using ;
using ;
using ;
using ;
using ;
using ;
namespace
{
/// <summary>
/// Standard Keyboard Shortcuts used by most applications
/// </summary>
public enum StandardShortcut
{
Copy,
Cut,
Paste,
SelectAll,
Save,
Open,
New,
Close,
Print
}
/// <summary>
/// Simulate keyboard key presses
/// </summary>
public static class KeyboardSimulator
{
#region Windows API Code
const int KEYEVENTF_EXTENDEDKEY = 0x1;
const int KEYEVENTF_KEYUP = 0x2;
[DllImport("")]
static extern void keybd_event(byte key, byte scan, int flags, int extraInfo);
#endregion
#region Methods
public static void KeyDown(Keys key)
{
keybd_event(ParseKey(key), 0, 0, 0);
}
public static void KeyUp(Keys key)
{
keybd_event(ParseKey(key), 0, KEYEVENTF_KEYUP, 0);
}
public static void KeyPress(Keys key)
{
KeyDown(key);
KeyUp(key);
}
public static void SimulateStandardShortcut(StandardShortcut shortcut)
{
switch (shortcut)
{
case :
KeyDown();
KeyPress();
KeyUp();
break;
case :
KeyDown();
KeyPress();
KeyUp();
break;
case :
KeyDown();
KeyPress();
KeyUp();
break;
case :
KeyDown();
KeyPress();
KeyUp();
break;
case :
KeyDown();
KeyPress();
KeyUp();
break;
case :
KeyDown();
KeyPress();
KeyUp();
break;
case :
KeyDown();
KeyPress();
KeyUp();
break;
case :
KeyDown();
KeyPress(Keys.F4);
KeyUp();
break;
case :
KeyDown();
KeyPress();
KeyUp();
break;
}
}
static byte ParseKey(Keys key)
{
// Alt, Shift, and Control need to be changed for API function to work with them
switch (key)
{
case :
return (byte)18;
case :
return (byte)17;
case :
return (byte)16;
default:
return (byte)key;
}
}
#endregion
}
}
MouseSimulator
using System;
using ;
using ;
using ;
using ;
namespace
{
/// <summary>
/// And X, Y point on the screen
/// </summary>
public struct MousePoint
{
public MousePoint(Point p)
{
X = ;
Y = ;
}
public int X;
public int Y;
public static implicit operator Point(MousePoint p)
{
return new Point(, );
}
}
/// <summary>
/// Mouse buttons that can be pressed
/// </summary>
public enum MouseButton
{
Left = 0x2,
Right = 0x8,
Middle = 0x20
}
/// <summary>
/// Operations that simulate mouse events
/// </summary>
public static class MouseSimulator
{
#region Windows API Code
[DllImport("")]
static extern int ShowCursor(bool show);
[DllImport("")]
static extern void mouse_event(int flags, int dX, int dY, int buttons, int extraInfo);
const int MOUSEEVENTF_MOVE = 0x1;
const int MOUSEEVENTF_LEFTDOWN = 0x2;
const int MOUSEEVENTF_LEFTUP = 0x4;
const int MOUSEEVENTF_RIGHTDOWN = 0x8;
const int MOUSEEVENTF_RIGHTUP = 0x10;
const int MOUSEEVENTF_MIDDLEDOWN = 0x20;
const int MOUSEEVENTF_MIDDLEUP = 0x40;
const int MOUSEEVENTF_WHEEL = 0x800;
const int MOUSEEVENTF_ABSOLUTE = 0x8000;
#endregion
#region Properties
/// <summary>
/// Gets or sets a structure that represents both X and Y mouse coordinates
/// </summary>
public static MousePoint Position
{
get
{
return new MousePoint();
}
set
{
= value;
}
}
/// <summary>
/// Gets or sets only the mouse's x coordinate
/// </summary>
public static int X
{
get
{
return ;
}
set
{
= new Point(value, Y);
}
}
/// <summary>
/// Gets or sets only the mouse's y coordinate
/// </summary>
public static int Y
{
get
{
return ;
}
set
{
= new Point(X, value);
}
}
#endregion
#region Methods
/// <summary>
/// Press a mouse button down
/// </summary>
/// <param name="button"></param>
public static void MouseDown(MouseButton button)
{
mouse_event(((int)button), 0, 0, 0, 0);
}
public static void MouseDown(MouseButtons button)
{
switch (button)
{
case :
MouseDown();
break;
case :
MouseDown();
break;
case :
MouseDown();
break;
}
}
/// <summary>
/// Let a mouse button up
/// </summary>
/// <param name="button"></param>
public static void MouseUp(MouseButton button)
{
mouse_event(((int)button) * 2, 0, 0, 0, 0);
}
public static void MouseUp(MouseButtons button)
{
switch (button)
{
case :
MouseUp();
break;
case :
MouseUp();
break;
case :
MouseUp();
break;
}
}
/// <summary>
/// Click a mouse button (down then up)
/// </summary>
/// <param name="button"></param>
public static void Click(MouseButton button)
{
MouseDown(button);
MouseUp(button);
}
public static void Click(MouseButtons button)
{
switch (button)
{
case :
Click();
break;
case :
Click();
break;
case :
Click();
break;
}
}
/// <summary>
/// Double click a mouse button (down then up twice)
/// </summary>
/// <param name="button"></param>
public static void DoubleClick(MouseButton button)
{
Click(button);
Click(button);
}
public static void DoubleClick(MouseButtons button)
{
switch (button)
{
case :
DoubleClick();
break;
case :
DoubleClick();
break;
case :
DoubleClick();
break;
}
}
/// <summary>
/// Show a hidden current on currently application
/// </summary>
public static void Show()
{
ShowCursor(true);
}
/// <summary>
/// Hide mouse cursor only on current application's forms
/// </summary>
public static void Hide()
{
ShowCursor(false);
}
#endregion
}
}
Test class:
using System;
using ;
using ;
using ;
using ;
using ;
using ;
using ;
using MouseKeyboardLibrary;
namespace SampleApplication
{
/*
The above 5 classes are compiled into Dll references, usage examples
*/
public partial class HookTestForm : Form
{
//Used to determine whether the window is automatically closed
mTimer = new ();
//Record the mouse and keyboard operation time to determine whether the Form will automatically close
private DateTime mRecordTime = ;
MouseHook mouseHook = new MouseHook();
KeyboardHook keyboardHook = new KeyboardHook();
public HookTestForm()
{
InitializeComponent();
}
private void TestForm_Load(object sender, EventArgs e)
{
+= mTimer_Tick;
= 2000;
(); //Start the timer
+= new MouseEventHandler(mouseHook_MouseMove);
+= new MouseEventHandler(mouseHook_MouseDown);
+= new MouseEventHandler(mouseHook_MouseUp);
+= new MouseEventHandler(mouseHook_MouseWheel);
+= new KeyEventHandler(keyboardHook_KeyDown);
+= new KeyEventHandler(keyboardHook_KeyUp);
+= new KeyPressEventHandler(keyboardHook_KeyPress);
();
();
SetXYLabel("", (), ());
}
void keyboardHook_KeyPress(object sender, KeyPressEventArgs e)
{
AddKeyboardEvent(
"KeyPress",
"",
(),
"",
"",
""
);
}
void keyboardHook_KeyUp(object sender, KeyEventArgs e)
{
AddKeyboardEvent(
"KeyUp",
(),
"",
(),
(),
()
);
}
void keyboardHook_KeyDown(object sender, KeyEventArgs e)
{
AddKeyboardEvent(
"KeyDown",
(),
"",
(),
(),
()
);
}
void mouseHook_MouseWheel(object sender, MouseEventArgs e)
{
AddMouseEvent(
"MouseWheel",
"",
"",
"",
()
);
}
void mouseHook_MouseUp(object sender, MouseEventArgs e)
{
AddMouseEvent(
"MouseUp",
(),
(),
(),
""
);
}
void mouseHook_MouseDown(object sender, MouseEventArgs e)
{
AddMouseEvent(
"MouseDown",
(),
(),
(),
""
);
}
void mouseHook_MouseMove(object sender, MouseEventArgs e)
{
SetXYLabel("", (), ());
}
void SetXYLabel(string keyName, string x, string y)
{
= $"Current keyName={keyName} Point: X={x}, y={y} {("HH:mm:ss")}";
mRecordTime = ;
}
void AddMouseEvent(string eventType, string button, string x, string y, string delta)
{
// = $"Current Mouse eventType ={eventType} button ={button} delta ={delta} X ={x}, y ={y}";
SetXYLabel(button, x, y);
}
void AddKeyboardEvent(string eventType, string keyCode, string keyChar, string shift, string alt, string control)
{
// = $"Current Mouse eventType ={eventType} keyCode ={keyCode} keyChar ={keyChar} shift ={shift}, alt ={alt} control={control}";
SetXYLabel(button, x, y);
}
private void TestForm_FormClosed(object sender, FormClosedEventArgs e)
{
// Not necessary anymore, will stop when application exits
//();
//();
}
private bool stopForm = false;
//Timer
private void mTimer_Tick(object sender, EventArgs e)
{
if(stopForm)
{
return;
}
if (( - mRecordTime).TotalMinutes >= 5) //Recording time is greater than 5 minutes
{
stopForm = true;
for (int i = - 1; i >= 0; i--)
{
Form item = [i];
if ( != && != "UserLogin") //Close the open form that is not the main form
{
();
}
}
= ;
stopForm = false;
}
}
}
}