Add full XML doc comments, file header with attribution, and inline comments

This commit is contained in:
2026-06-30 17:08:02 -07:00
parent a8de8f95c0
commit 63ea7f8cda

View File

@@ -1,27 +1,66 @@
using System.Diagnostics; // FuckCapsLock — Permanently disable the vestigial Caps Lock key.
// Copyright (c) 2026 Llama Chile Shop. MIT License.
//
// This application was pair-programmed by:
// gramps@llamachile.shop (senior developer, direct supervision)
// opencode (AI pair programmer)
//
// Under direct, senior supervision, paired programming can be an
// effective and efficient tool.
//
// Installs a low-level keyboard hook (WH_KEYBOARD_LL) that swallows
// VK_CAPITAL (0x14) before it reaches any window. Runs in the system
// tray until the user exits via the context menu.
//
// System calls reference:
// SetWindowsHookEx / UnhookWindowsHookEx / CallNextHookEx — user32.dll
// GetModuleHandle — kernel32.dll
// Registry.CurrentUser\...\Run — auto-start on boot
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.Win32; using Microsoft.Win32;
static class Program static class Program
{ {
/// <summary>Low-level keyboard hook identifier — intercepts keys before any window.</summary>
const int WH_KEYBOARD_LL = 13; const int WH_KEYBOARD_LL = 13;
/// <summary>Virtual-key code for Caps Lock — the only key this app blocks.</summary>
const int VK_CAPITAL = 0x14; const int VK_CAPITAL = 0x14;
/// <summary>Callback signature for LowLevelKeyboardProc used by SetWindowsHookEx.</summary>
/// <param name="nCode">Hook code — >= 0 means the hook should process the message.</param>
/// <param name="wParam">Message type (WM_KEYDOWN, WM_KEYUP, etc.).</param>
/// <param name="lParam">Pointer to a KBDLLHOOKSTRUCT with the raw key data.</param>
/// <returns>1 to swallow the key, or CallNextHookEx to let it through.</returns>
delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
/// <summary>Installs a low-level keyboard hook into the global hook chain.</summary>
/// <param name="idHook">Hook type — WH_KEYBOARD_LL (13) for keyboard events.</param>
/// <param name="lpfn">Pointer to the callback function.</param>
/// <param name="hMod">Module handle for the current process.</param>
/// <param name="dwThreadId">0 = hook applies globally to all threads in this desktop.</param>
/// <returns>Hook handle on success, IntPtr.Zero on failure.</returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
/// <summary>Removes a previously installed hook from the hook chain.</summary>
/// <param name="hhk">Hook handle returned by SetWindowsHookEx.</param>
/// <returns>True on success.</returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
static extern bool UnhookWindowsHookEx(IntPtr hhk); static extern bool UnhookWindowsHookEx(IntPtr hhk);
/// <summary>Passes the hook event to the next hook in the chain.</summary>
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
/// <summary>Retrieves the module handle for the calling process — required by SetWindowsHookEx.</summary>
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr GetModuleHandle(string lpModuleName); static extern IntPtr GetModuleHandle(string lpModuleName);
/// <summary>Structure deserialized from the lParam pointer in the hook callback.</summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct KBDLLHOOKSTRUCT struct KBDLLHOOKSTRUCT
{ {
@@ -32,30 +71,44 @@ static class Program
public IntPtr dwExtraInfo; public IntPtr dwExtraInfo;
} }
/// <summary>Cached delegate — prevents garbage collection of the callback.</summary>
static LowLevelKeyboardProc _proc = HookCallback; static LowLevelKeyboardProc _proc = HookCallback;
/// <summary>Active hook handle returned by SetWindowsHookEx.</summary>
static IntPtr _hookId = IntPtr.Zero; static IntPtr _hookId = IntPtr.Zero;
/// <summary>System tray icon — visible while the app is running.</summary>
static NotifyIcon? _trayIcon; static NotifyIcon? _trayIcon;
/// <summary>
/// Entry point. Registers auto-start, enforces a single instance via a
/// named mutex, installs the keyboard hook, and runs the WinForms message
/// loop. The app lives in the system tray until Exit is selected.
/// </summary>
[STAThread] [STAThread]
static void Main() static void Main()
{ {
RegisterStartup(); RegisterStartup();
// Named mutex prevents multiple instances — second launch exits silently.
using var mutex = new Mutex(true, @"Global\FuckCapsLock", out var createdNew); using var mutex = new Mutex(true, @"Global\FuckCapsLock", out var createdNew);
if (!createdNew) if (!createdNew)
return; return;
// Install the low-level hook; bail if it fails (e.g., insufficient permissions).
_hookId = SetHook(_proc); _hookId = SetHook(_proc);
if (_hookId == IntPtr.Zero) if (_hookId == IntPtr.Zero)
return; return;
ApplicationConfiguration.Initialize(); ApplicationConfiguration.Initialize();
// Hidden window — required to pump messages and keep the hook alive.
using var form = new Form(); using var form = new Form();
form.WindowState = FormWindowState.Minimized; form.WindowState = FormWindowState.Minimized;
form.ShowInTaskbar = false; form.ShowInTaskbar = false;
form.Load += (_, _) => form.Hide(); form.Load += (_, _) => form.Hide();
// Tray icon with Exit context menu to cleanly unhook and quit.
_trayIcon = new NotifyIcon _trayIcon = new NotifyIcon
{ {
Icon = SystemIcons.Application, Icon = SystemIcons.Application,
@@ -69,11 +122,18 @@ static class Program
}); });
_trayIcon.Visible = true; _trayIcon.Visible = true;
// Blocks until the form closes (Exit is clicked or process is killed).
Application.Run(form); Application.Run(form);
// Remove the hook when the app exits.
UnhookWindowsHookEx(_hookId); UnhookWindowsHookEx(_hookId);
} }
/// <summary>
/// Writes the current executable path to HKCU\Software\Microsoft\Windows\
/// CurrentVersion\Run so the app auto-starts on login. Silently ignores
/// errors (no admin rights needed for HKCU).
/// </summary>
static void RegisterStartup() static void RegisterStartup()
{ {
try try
@@ -85,8 +145,10 @@ static class Program
var path = Environment.ProcessPath; var path = Environment.ProcessPath;
if (path is null) return; if (path is null) return;
// Quote the path if it contains spaces (standard Windows practice).
var value = path.Contains(' ') ? $"\"{path}\"" : path; var value = path.Contains(' ') ? $"\"{path}\"" : path;
// Only write if the path has changed — avoids unnecessary registry churn.
var existing = key.GetValue("FuckCapsLock") as string; var existing = key.GetValue("FuckCapsLock") as string;
if (!string.Equals(existing, value, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(existing, value, StringComparison.OrdinalIgnoreCase))
key.SetValue("FuckCapsLock", value); key.SetValue("FuckCapsLock", value);
@@ -94,6 +156,11 @@ static class Program
catch { } catch { }
} }
/// <summary>
/// Installs the low-level keyboard hook using SetWindowsHookEx.
/// </summary>
/// <param name="proc">Delegate to the callback that processes each key event.</param>
/// <returns>Hook handle or IntPtr.Zero if installation failed.</returns>
static IntPtr SetHook(LowLevelKeyboardProc proc) static IntPtr SetHook(LowLevelKeyboardProc proc)
{ {
using var curProcess = Process.GetCurrentProcess(); using var curProcess = Process.GetCurrentProcess();
@@ -102,13 +169,22 @@ static class Program
GetModuleHandle(curModule!.ModuleName), 0); GetModuleHandle(curModule!.ModuleName), 0);
} }
/// <summary>
/// Hook callback invoked for every keyboard event. If the key is VK_CAPITAL (Caps Lock),
/// the event is swallowed by returning 1 without calling CallNextHookEx. All other keys
/// pass through normally.
/// </summary>
/// <param name="nCode">>= 0 means the hook should process the event.</param>
/// <param name="wParam">Window message type (WM_KEYDOWN, WM_SYSKEYUP, etc.).</param>
/// <param name="lParam">Pointer to a KBDLLHOOKSTRUCT with the raw key data.</param>
/// <returns>1 if Caps Lock was blocked; otherwise the result of CallNextHookEx.</returns>
static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{ {
if (nCode >= 0) if (nCode >= 0)
{ {
var hookStruct = Marshal.PtrToStructure<KBDLLHOOKSTRUCT>(lParam); var hookStruct = Marshal.PtrToStructure<KBDLLHOOKSTRUCT>(lParam);
if (hookStruct.vkCode == VK_CAPITAL) if (hookStruct.vkCode == VK_CAPITAL)
return (IntPtr)1; return (IntPtr)1; // Swallow the key — never forward it.
} }
return CallNextHookEx(_hookId, nCode, wParam, lParam); return CallNextHookEx(_hookId, nCode, wParam, lParam);
} }