/// <summary> /// A callback function which will be called every time a keyboard activity detected. /// https://docs.microsoft.com/en-us/windows/win32/winmsg/lowlevelkeyboardproc /// </summary> /// <param name="code"> /// Specifies whether the hook procedure must process the message. /// If code is HC_ACTION, the hook procedure must process the message. /// If code is less than zero, the hook procedure must pass the message to the /// CallNextHookEx function without further processing and must return the /// value returned by CallNextHookEx. /// </param> /// <param name="wParam"> /// Specifies whether the message was sent by the current thread. /// If the message was sent by the current thread, it is nonzero; otherwise, it is zero. /// </param> /// <param name="lParam"> /// Pointer to a CWPSTRUCT structure that contains details about the message. /// </param> /// <returns> /// If code is less than zero, the hook procedure must return the value returned by CallNextHookEx. /// If code is greater than or equal to zero, it is highly recommended that you call CallNextHookEx /// and return the value it returns; otherwise, other applications that have installed WH_CALLWNDPROC /// hooks will not receive hook notifications and may behave incorrectly as a result. If the hook /// procedure does not call CallNextHookEx, the return value should be zero. /// </returns> private IntPtr KeyboardHookProc(int code, uint wParam, IntPtr lParam) { //Indicates if any of the underlaying events set the e.Handled flag. var handled = false; //If it was Ok and there are no listeners. if (code < 0 || KeyDown == null && KeyUp == null && KeyPress == null) { return(User32.CallNextHookEx(_keyboardHookHandle, code, wParam, lParam)); } //Read structure KeyboardHookStruct at lParam var keyboard = (KeyboardHook)Marshal.PtrToStructure(lParam, typeof(KeyboardHook)); var isInjected = (keyboard.Flags & 0x10) != 0; if (KeyDown != null && (wParam == MessageKeydown || wParam == MessageSystemKeyDown)) { #region Raise KeyDown var isDownShift = (User32.GetKeyState(KeyShift) & 0x80) == 0x80; var isDownCapslock = User32.GetKeyState(KeyCapital) != 0; var e = new CustomKeyEventArgs(KeyInterop.KeyFromVirtualKey(keyboard.KeyCode), isDownCapslock ^ isDownShift, isInjected); KeyDown?.Invoke(this, e); handled = e.Handled; #endregion } if (KeyPress != null && wParam == MessageKeydown) { #region Raise KeyPress var isDownShift = (User32.GetKeyState(KeyShift) & 0x80) == 0x80; var isDownCapslock = User32.GetKeyState(KeyCapital) != 0; var keyState = new byte[256]; User32.GetKeyboardState(keyState); var inBuffer = new byte[2]; if (User32.ToAscii(keyboard.KeyCode, keyboard.ScanCode, keyState, inBuffer, keyboard.Flags) == 1) { var key = (char)inBuffer[0]; if (isDownCapslock ^ isDownShift && char.IsLetter(key)) { key = char.ToUpper(key); } var e = new CustomKeyPressEventArgs(key); KeyPress?.Invoke(this, e); handled = handled || e.Handled; } #endregion } if (KeyUp != null && (wParam == MessageKeyUp || wParam == MessageSystemKeyUp)) { #region Raise KeyUp var e = new CustomKeyEventArgs(KeyInterop.KeyFromVirtualKey(keyboard.KeyCode), false, isInjected); KeyUp?.Invoke(this, e); handled = handled || e.Handled; #endregion } //If event handled in application do not handoff to other listeners. return(handled ? new IntPtr(1) : User32.CallNextHookEx(_keyboardHookHandle, code, wParam, lParam)); }