public IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam) { var fEatKeyStroke = false; var wparamTyped = wParam.ToInt32(); if (Enum.IsDefined(typeof(KeyboardState), wparamTyped)) { var o = Marshal.PtrToStructure(lParam, typeof(LowLevelKeyboardInputEvent)); var p = (LowLevelKeyboardInputEvent)o; var keyState = (KeyboardState)wparamTyped; var keyUp = keyState == KeyboardState.KeyUp || keyState == KeyboardState.SysKeyUp; var keyDown = keyState == KeyboardState.KeyDown || keyState == KeyboardState.SysKeyDown; var key = (System.Windows.Forms.Keys)p.VirtualCode; var state = keyUp ? KeyboardKeyState.Up : keyDown ? KeyboardKeyState.Down : KeyboardKeyState.Unknown; Console.WriteLine("---"); var flags = new LowLevelKeyboardInputEventFlags(p.Flags); OnKeyEvent?.Invoke(new KeyboardKey { IsInjected = flags.IsInjected, Value = key, State = state }); } return(fEatKeyStroke ? (IntPtr)1 : CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam)); }
/// <summary> /// HandlesKeyDown and KeyUp events for defined keys. /// When key has been depressed for configured period of time, /// it plays a sound (if so configured) and upon KeyUp send the /// key press to the system. /// If the configured period of time is less than 0, they key will be ignored /// and never reach the system. /// </summary> /// <param name="nCode">Can be HC_ACTION or HC_NOREMOVE. If less than 0 the hook procedure must pass the message to the CallNextHookEx function without further processing and return the value returned by CallNextHookEx</param> /// <param name="wParam">Int32 value that contains they virtual key code</param> /// <param name="lParam">Pointer to a struct LowLevelKeyboardInputEvent containing additional information</param> /// <returns>1 if the event should no be processed any further by the system, or the result of calling CallNextHookEx, which will call other hooks</returns> private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { // get the key int wparamTyped = wParam.ToInt32(); KeyboardState?kbdState = null; if (Enum.IsDefined(typeof(KeyboardState), wparamTyped)) { kbdState = (KeyboardState)wparamTyped; } // get additional information object o = Marshal.PtrToStructure(lParam, typeof(LowLevelKeyboardInputEvent)); LowLevelKeyboardInputEvent p = (LowLevelKeyboardInputEvent)o; int vkCode = p.VirtualCode; Keys vkKey = (Keys)vkCode; LowLevelKeyboardInputEventFlags flags = (LowLevelKeyboardInputEventFlags)p.Flags; if (nCode >= 0 && // as per spec, values less than 0 indicate that event should not be processed and CallNextHookEx be called immediately (!flags.HasFlag(LowLevelKeyboardInputEventFlags.LLKHF_INJECTED)) && // not injected, e. g. generated by us kbdState != null && (kbdState == KeyboardState.KeyDown || kbdState == KeyboardState.KeyUp) && // valid keyboard state KeyMap.dtKeys.Keys.Contains(vkKey) && // handled key getKeyDelay(vkKey) != 0) // key has a delay assigned (< 0 means deactivated, 0 means no delay) { //Debug.WriteLine("Timestamp: " + p.TimeStamp.ToString()); //Debug.WriteLine("Extra Info: " + p.AdditionalInformation.ToString()); //Debug.WriteLine("Injected: " + (((p.Flags & 0x00000010) > 0) ? "yes" : "no")); if (kbdState == KeyboardState.KeyDown) { if (KeyMap.dtKeys[vkKey].started == null) { // remember first time they key was pressed (event will be repeated while it is kept down) KeyMap.dtKeys[vkKey].started = DateTime.Now; } else if (Properties.Settings.Default.playSound && !soundPlayed && pressDurationSufficient(KeyMap.dtKeys[vkKey].started, vkKey)) { // play a sound when configured delay has been reached Sound.play(); // only play once, when the use keeps holding the key down soundPlayed = true; } } else if (kbdState == KeyboardState.KeyUp) { DateTime?started = KeyMap.dtKeys[vkKey].started; // reset time the key was first pressed KeyMap.dtKeys[vkKey].started = null; // reset soun played soundPlayed = false; if (pressDurationSufficient(started, vkKey)) { // toggle Debug.WriteLine("toggled"); // simulate the key being pressed, will have flag set to LLKHF_INJECTED, // so the event will not be handled by us, othewise the cat would bite its own tail pressKey(vkKey); } } // cancel event propagation, aka "eat" event return((IntPtr)1); } // we did not handle the event, pass it on return(CallNextHookEx(hookID, nCode, wParam, lParam)); }