中年プログラマーの息抜き

ブログをはじめました。気の向くままにプログラム関連ネタをメモしていきます。

【C#】キーフック-左CapsLockを無効にして左Ctrlキーに偽装してみた

早速経緯から、

つい最近までHHKBキーボードを使っていましたが、パワポやワードなど資料作成時間が増えたことから矢印キー(↑→↓←)の利用頻度が増えます。

長時間作業すると 右手首が痛いんじゃ 状態

※HHKBでは、矢印キーは「Fn」割り当て

 

そんな理由から、手元でほこりをかぶっていたRealForce108UBKキーボードを使うようにしたのですが、これCapsLockと左Controlキーの割り当て変更が出来ません。

 

HHKBキーボードの左Ctrlキーに慣れきっているのでタイピング中の手の位置も中央付近が習慣付けされていて、こうなると左下にある左Ctrlキーを使うときの違和感が半端ないんですよね。さらに、間違えてCapsLockしてしまうことでイライラ

そして、長時間作業すると 左手首が痛いんじゃ 状態

 

キーボード買い換えも考えますが、そんな余裕もないしそもそもこんな理由で買い替えるなんていやだしなぁといった感じで我慢して何日か作業したんですが、どうにも慣れない機材を使うと作業時間に比例してイライラ

※少し使うと慣れてくるかと思いましたが、全然でした。

 

ということで、プログラム書いてCapsLockキー入力を無効にして使ってます。

※単に無効化するだけでは面白くなかったので「CtrlLockキー」にしてしまいました。

 

以下C#ソースコード

public static class KeyboardHook { [StructLayout(LayoutKind.Sequential)] private struct KBDLLHOOKSTRUCT { public uint vkCode; public uint scanCode; public uint flags; public uint time; public UIntPtr dwExtraInfo; } [StructLayout(LayoutKind.Sequential)] private struct KEYBDINPUT { public ushort wVk; public ushort wScan; public uint dwFlags; public uint time; public UIntPtr dwExtraInfo; public int dummy1; public int dummy2; }; [StructLayout(LayoutKind.Sequential)] private struct INPUT { public int type; public KEYBDINPUT ki; }; [DllImport("user32.dll")] private static extern IntPtr SetWindowsHookEx(int idHook, KeyboardProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll")] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll")] private static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("user32.dll")] private static extern uint SendInput(int nInputs, INPUT pInputs, int cbsize); private const int WH_KEYBOARD_LL = 0x000D; private static readonly IntPtr TRUE = new IntPtr(1); private static readonly IntPtr WM_KEYDOWN = new IntPtr(0x0100); private static readonly IntPtr WM_KEYUP = new IntPtr(0x0101); private static readonly IntPtr WM_SYSKEYDOWN = new IntPtr(0x0104); private static readonly IntPtr WM_SYSKEYUP = new IntPtr(0x0105); private static readonly INPUT CTRL_D = { new INPUT { type = 1, ki = new KEYBDINPUT() { wVk = 162, wScan = 0, dwFlags = 0, time = 0, dwExtraInfo = UIntPtr.Zero } } }; private static readonly INPUT[] CTRL_U = { new INPUT { type = 1, ki = new KEYBDINPUT() { wVk = 162, wScan = 0, dwFlags = 2, time = 0, dwExtraInfo = UIntPtr.Zero } } }; private static readonly int SZ_INPUT = Marshal.SizeOf(typeof(INPUT)); private delegate IntPtr KeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); private static readonly KeyboardProc proc = Proc; private static IntPtr sHookId = IntPtr.Zero; private static bool sHoldCtrl = false; public static void Start() { if (sHookId != IntPtr.Zero) return; using (var p = Process.GetCurrentProcess()) using (var m = p.MainModule) sHookId = SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(m.ModuleName), 0); } public static void Stop() { UnhookWindowsHookEx(sHookId); sHookId = IntPtr.Zero; if (sHoldCtrl) SendInput(1, CTRL_U, SZ_INPUT); } private static IntPtr Proc(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode < 0) return CallNextHookEx(sHookId, nCode, wParam, lParam); if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) { var kb = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); var vkCode = kb.vkCode; if (vkCode == 20) { SendInput(1, CTRL_D, SZ_INPUT); sHoldCtrl = true; return TRUE; } else if (vkCode == 240 || vkCode == 242) { if (sHoldCtrl) SendInput(1, CTRL_U, SZ_INPUT); return TRUE; } else if (vkCode == 162) { if (sHoldCtrl) SendInput(1, CTRL_U, SZ_INPUT); } } else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) { var kb = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); var vkCode = kb.vkCode; if (vkCode == 20 || vkCode == 240 || vkCode == 242) { if (sHoldCtrl) SendInput(1, CTRL_U, SZ_INPUT); return TRUE; } else if (vkCode == 162) { sHoldCtrl = false; } } return CallNextHookEx(sHookId, nCode, wParam, lParam); } }

KeyboardHookクラスリファレンス

CapsLockキー入力を無効化します。

CapsLockキー入力で状態を切り替えます。

 → Ctrlキーロック状態をエミュレート

 → Ctrlキーロック状態をリリース

  • KeyboardHook.Start() 
    グローバルフック(キーフック)開始

  • KeyboardHook.Stop()
    グローバルフック(キーフック)停止