Map capslock to control in Windows 10 (2025)

Several solutions. None of these require rebooting, since they use keyboard hook interface.

If you want to remap Caps Lock to Ctrl for programs running as administrator, you also need to run this program as administrator.

Uncap

Download uncap.exe from https://github.com/susam/uncap/releases , then in the terminal type uncap 20:17. It will close the terminal (cmd or powershell), but keep running in the background.

Type uncap --help for help.

dual-key-remap

Download from https://github.com/ililim/dual-key-remap/ .

Solution in PowerShell

The technique to use Add-Type to run arbitrary C# code is taken fromhttps://www.tarlogic.com/blog/how-to-make-keylogger-in-powershell/https://hinchley.net/articles/creating-a-key-logger-via-a-global-system-hook-using-powershell.

Just type the following in PowerShell. (To open PowerShell, press Windows+X, then select "PowerShell")

Add-Type @" using System; using System.Runtime.InteropServices; using System.Windows.Forms; public class CapsLockToCtrl { private const int WH_KEYBOARD_LL = 13; private const int WM_KEYDOWN = 0x100; private const int WM_KEYUP = 0x101; private const int VK_CAPITAL = 0x14; private const int VK_CONTROL = 0x11; private const int KEYEVENTF_KEYUP = 0x2; public static void Main() { IntPtr hookId = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookCallback, GetModuleHandle(null), 0); Application.Run(); // unless there's Application.Exit() call somewhere this will run indefinitely UnhookWindowsHookEx(hookId); } private delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam); // https://stackoverflow.com/a/3146691 [StructLayout(LayoutKind.Sequential)] private struct KBDLLHOOKSTRUCT { public uint vkCode; public uint scanCode; public uint flags; public uint time; public IntPtr dwExtraInfo; } private static IntPtr KeyboardHookCallback(int nCode, UIntPtr wParam, IntPtr lParam) { // if (nCode >= 0 && (wParam == (UIntPtr)WM_KEYDOWN || wParam == (UIntPtr)WM_KEYUP)) { KBDLLHOOKSTRUCT kbdStruct = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if (kbdStruct.vkCode == VK_CAPITAL) { keybd_event(VK_CONTROL, 0, ((int)wParam == WM_KEYDOWN) ? 0u : KEYEVENTF_KEYUP, 0); return (IntPtr)1; // Block the original key press } } return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam); } [DllImport("user32.dll")] private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll")] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll")] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll")] private static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("user32.dll")] private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo); }"@ -ReferencedAssemblies System.Windows.Forms[CapsLockToCtrl]::Main()

Keep the window open (possibly minimized), otherwise it will stop working.

Or, minified version if you prefer. (in case you cannot copy-paste/have Internet access and have to type in the code manually)

Add-Type @"using System;using System.Runtime.InteropServices;using System.Windows.Forms;public class C { public static void Main() { SetWindowsHookEx(13, B, GetModuleHandle(null), 0); Application.Run(); } delegate IntPtr L(int n, UIntPtr w, IntPtr l); private static IntPtr B(int n, UIntPtr w, IntPtr l) { if ((int)w<258&&Marshal.ReadInt32(l)==20){ keybd_event(17, 0, (int)w==256?0u:2, 0); return (IntPtr) 1; } return CallNextHookEx((IntPtr)0, n, w, l); } [DllImport("user32.dll")] static extern IntPtr SetWindowsHookEx(int i, L f, IntPtr h, uint d); [DllImport("user32.dll")] static extern IntPtr CallNextHookEx(IntPtr i, int n, UIntPtr w, IntPtr l); [DllImport("kernel32.dll")] static extern IntPtr GetModuleHandle(string n); [DllImport("user32.dll")] static extern void keybd_event(byte v, byte s, uint f, uint e);}"@ -ReferencedAssemblies System.Windows.Forms[C]::Main()

Solution in C#

Save the following into a file named a.cs.

using System;using System.Runtime.InteropServices;using System.Windows.Forms;public class CapsLockToCtrl { private const int WH_KEYBOARD_LL = 13; private const int WM_KEYDOWN = 0x100; private const int WM_KEYUP = 0x101; private const int VK_CAPITAL = 0x14; private const int VK_CONTROL = 0x11; private const int KEYEVENTF_KEYUP = 0x2; public static void Main() { IntPtr hookId = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookCallback, GetModuleHandle(null), 0); Application.Run(); // unless there's Application.Exit() call somewhere this will run indefinitely UnhookWindowsHookEx(hookId); } private delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam); // https://stackoverflow.com/a/3146691 [StructLayout(LayoutKind.Sequential)] private struct KBDLLHOOKSTRUCT { public uint vkCode; public uint scanCode; public uint flags; public uint time; public IntPtr dwExtraInfo; } private static IntPtr KeyboardHookCallback(int nCode, UIntPtr wParam, IntPtr lParam) { // if (nCode >= 0 && (wParam == (UIntPtr)WM_KEYDOWN || wParam == (UIntPtr)WM_KEYUP)) { KBDLLHOOKSTRUCT kbdStruct = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if (kbdStruct.vkCode == VK_CAPITAL) { keybd_event(VK_CONTROL, 0, ((int)wParam == WM_KEYDOWN) ? 0u : KEYEVENTF_KEYUP, 0); return (IntPtr)1; // Block the original key press } } return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam); } [DllImport("user32.dll")] private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll")] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll")] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll")] private static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("user32.dll")] private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);}

Then in a terminal type:

"C:\Windows\Microsoft.NET\Framework\v3.5\csc.exe" a.csa.exe

The caveat with having to keep the window open, and using Ctrl+C instead of Caps+C to terminate the program, applies.

Solution in C

This will only work if there's a C compiler installed.

Quite similar to the answer above https://superuser.com/a/1490007/577463 , but minified in case you cannot copy-paste/have Internet access and have to type in the code manually. The difference is that Ctrl is not mapped back to Caps Lock.

Save the following to a file named for example a.c.

#include<windows.h>LRESULT f(int n,WPARAM w,LPARAM l){ return n>=0&&w<258&&*(int*)l==20 ? keybd_event(17,0,w%2*2,0),1: CallNextHookEx(0,n,w,l); }int main(){ SetWindowsHookEx(13,f,GetModuleHandle(0),0); MSG m;while(GetMessage(&m,0,0,0)) TranslateMessage(&m),DispatchMessage(&m); }

Then type in the terminal gcc a.c -o a, press enter, then type a, press enter.

Note that if you use Caps+C to terminate the program a, you can, but then the Ctrl key will not be released. Use Ctrl+C to terminate it instead.

Unminified version

#include <Windows.h>LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode >= 0 && (wParam == WM_KEYDOWN || wParam == WM_KEYUP)) { KBDLLHOOKSTRUCT* kbdStruct = (KBDLLHOOKSTRUCT*)lParam; if (kbdStruct->vkCode == VK_CAPITAL) { // keybd_event(VK_CONTROL, 0, wParam == WM_KEYUP ? KEYEVENTF_KEYUP : 0, 0); INPUT input = {.type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL, .dwFlags = wParam == WM_KEYUP ? KEYEVENTF_KEYUP : 0 }}; SendInput(1, &input, sizeof(INPUT)); return 1; // Block the original key press } } return CallNextHookEx(NULL, nCode, wParam, lParam);}int main() { HHOOK hookId = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0); MSG msg; while (GetMessage(&msg, NULL, 0, 0) != 0) { TranslateMessage(&msg); DispatchMessage(&msg); } UnhookWindowsHookEx(hookId); return 0;}
Map capslock to control in Windows 10 (2025)
Top Articles
Latest Posts
Recommended Articles
Article information

Author: Mrs. Angelic Larkin

Last Updated:

Views: 6157

Rating: 4.7 / 5 (67 voted)

Reviews: 82% of readers found this page helpful

Author information

Name: Mrs. Angelic Larkin

Birthday: 1992-06-28

Address: Apt. 413 8275 Mueller Overpass, South Magnolia, IA 99527-6023

Phone: +6824704719725

Job: District Real-Estate Facilitator

Hobby: Letterboxing, Vacation, Poi, Homebrewing, Mountain biking, Slacklining, Cabaret

Introduction: My name is Mrs. Angelic Larkin, I am a cute, charming, funny, determined, inexpensive, joyous, cheerful person who loves writing and wants to share my knowledge and understanding with you.