C#
programming
keyboard capture
software development
global keyboard hook

Global keyboard capture in C application

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In C#, global keyboard capture on Windows usually means installing a low-level keyboard hook so your application can observe key presses even when it is not the active window. The standard mechanism is the Win32 WH_KEYBOARD_LL hook via SetWindowsHookEx, but it must be used carefully because it affects system-wide input flow.

How global keyboard capture works

Windows exposes hook chains that let applications inspect input events before they reach the target application. For keyboard capture in managed code, the usual choice is the low-level keyboard hook type WH_KEYBOARD_LL.

The basic flow is:

  • define a hook callback
  • install it with SetWindowsHookEx
  • keep the process alive with a message loop
  • remove the hook when shutting down

Without that lifecycle, the hook is unreliable or leaks system resources.

A minimal C# example

csharp
1using System;
2using System.Diagnostics;
3using System.Runtime.InteropServices;
4
5class Program
6{
7    private const int WH_KEYBOARD_LL = 13;
8    private const int WM_KEYDOWN = 0x0100;
9
10    private static LowLevelKeyboardProc _proc = HookCallback;
11    private static IntPtr _hookId = IntPtr.Zero;
12
13    static void Main()
14    {
15        _hookId = SetHook(_proc);
16        Console.WriteLine("Hook installed. Press Enter to exit.");
17        Console.ReadLine();
18        UnhookWindowsHookEx(_hookId);
19    }
20
21    private static IntPtr SetHook(LowLevelKeyboardProc proc)
22    {
23        using Process curProcess = Process.GetCurrentProcess();
24        using ProcessModule curModule = curProcess.MainModule!;
25        return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
26    }
27
28    private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
29
30    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
31    {
32        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
33        {
34            int vkCode = Marshal.ReadInt32(lParam);
35            Console.WriteLine((ConsoleKey)vkCode);
36        }
37
38        return CallNextHookEx(_hookId, nCode, wParam, lParam);
39    }
40
41    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
42    private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
43
44    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
45    [return: MarshalAs(UnmanagedType.Bool)]
46    private static extern bool UnhookWindowsHookEx(IntPtr hhk);
47
48    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
49    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
50
51    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
52    private static extern IntPtr GetModuleHandle(string? lpModuleName);
53}

This is enough to demonstrate the mechanism, though production code should have stronger lifecycle and error handling.

Why the callback must be lightweight

The hook callback runs in a very sensitive path. If you block, log too aggressively, or do heavy work there, you can make keyboard input feel delayed for the entire system.

A good pattern is:

  • capture only the event data you need
  • queue it quickly
  • process it elsewhere on another thread

Treat the hook callback as a fast handoff point, not as a place for real business logic.

Always unhook cleanly

Installing the hook is only half the job. You must call UnhookWindowsHookEx when the application exits or the hook can remain active until the process ends unexpectedly.

If you build this into a WinForms or WPF application, dispose the hook in the application shutdown path instead of leaving it to chance.

Security and product concerns

Global keyboard capture has obvious privacy implications. Some legitimate apps need it, such as hotkey managers, accessibility tools, input remappers, and some game utilities, but the same mechanism is also associated with malware and keylogging.

That means you should:

  • make the feature explicit to users
  • avoid capturing more data than necessary
  • secure any stored input data
  • ensure your application has a legitimate reason to use a global hook

Common Pitfalls

  • Doing heavy work inside the hook callback and causing noticeable input lag.
  • Forgetting to call CallNextHookEx, which can interfere with other hooks.
  • Installing the hook but never unhooking it cleanly on shutdown.
  • Assuming global hooks are portable beyond Windows. This technique is Win32-specific.
  • Treating keyboard capture as harmless UI plumbing when it has real privacy and security implications.

Summary

  • Global keyboard capture in C# on Windows usually uses WH_KEYBOARD_LL with SetWindowsHookEx.
  • The hook callback should be fast and minimal.
  • Always call CallNextHookEx and clean up with UnhookWindowsHookEx.
  • This is a Windows-specific technique, not a general C# feature.
  • Because it captures system-wide input, it should be used carefully and transparently.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.