// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or https://mit-license.org/
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Gma.System.MouseKeyHook.Implementation;
using Gma.System.MouseKeyHook.WinApi;
namespace Gma.System.MouseKeyHook
{
///
/// Provides extended argument data for the or
/// event.
///
public class KeyEventArgsExt : KeyEventArgs
{
///
/// Initializes a new instance of the class.
///
///
public KeyEventArgsExt(Keys keyData)
: base(keyData)
{
}
internal KeyEventArgsExt(Keys keyData, int scanCode, int timestamp, bool isKeyDown, bool isKeyUp,
bool isExtendedKey)
: this(keyData)
{
ScanCode = scanCode;
Timestamp = timestamp;
IsKeyDown = isKeyDown;
IsKeyUp = isKeyUp;
IsExtendedKey = isExtendedKey;
}
///
/// The hardware scan code.
///
public int ScanCode { get; }
///
/// The system tick count of when the event occurred.
///
public int Timestamp { get; }
///
/// True if event signals key down..
///
public bool IsKeyDown { get; }
///
/// True if event signals key up.
///
public bool IsKeyUp { get; }
///
/// True if event signals, that the key is an extended key
///
public bool IsExtendedKey { get; }
internal static KeyEventArgsExt FromRawDataApp(CallbackData data)
{
var wParam = data.WParam;
var lParam = data.LParam;
//http://msdn.microsoft.com/en-us/library/ms644984(v=VS.85).aspx
const uint maskKeydown = 0x40000000; // for bit 30
const uint maskKeyup = 0x80000000; // for bit 31
const uint maskExtendedKey = 0x1000000; // for bit 24
var timestamp = Environment.TickCount;
var flags = (uint) lParam.ToInt64();
//bit 30 Specifies the previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up.
var wasKeyDown = (flags & maskKeydown) > 0;
//bit 31 Specifies the transition state. The value is 0 if the key is being pressed and 1 if it is being released.
var isKeyReleased = (flags & maskKeyup) > 0;
//bit 24 Specifies the extended key state. The value is 1 if the key is an extended key, otherwise the value is 0.
var isExtendedKey = (flags & maskExtendedKey) > 0;
var keyData = AppendModifierStates((Keys) wParam);
var scanCode = (int) (((flags & 0x10000) | (flags & 0x20000) | (flags & 0x40000) | (flags & 0x80000) |
(flags & 0x100000) | (flags & 0x200000) | (flags & 0x400000) | (flags & 0x800000)) >>
16);
var isKeyDown = !isKeyReleased;
var isKeyUp = wasKeyDown && isKeyReleased;
return new KeyEventArgsExt(keyData, scanCode, timestamp, isKeyDown, isKeyUp, isExtendedKey);
}
internal static KeyEventArgsExt FromRawDataGlobal(CallbackData data)
{
var wParam = data.WParam;
var lParam = data.LParam;
var keyboardHookStruct =
(KeyboardHookStruct) Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
var keyData = AppendModifierStates((Keys) keyboardHookStruct.VirtualKeyCode);
var keyCode = (int) wParam;
var isKeyDown = keyCode == Messages.WM_KEYDOWN || keyCode == Messages.WM_SYSKEYDOWN;
var isKeyUp = keyCode == Messages.WM_KEYUP || keyCode == Messages.WM_SYSKEYUP;
const uint maskExtendedKey = 0x1;
var isExtendedKey = (keyboardHookStruct.Flags & maskExtendedKey) > 0;
return new KeyEventArgsExt(keyData, keyboardHookStruct.ScanCode, keyboardHookStruct.Time, isKeyDown,
isKeyUp, isExtendedKey);
}
// # It is not possible to distinguish Keys.LControlKey and Keys.RControlKey when they are modifiers
// Check for Keys.Control instead
// Same for Shift and Alt(Menu)
// See more at http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.framework.windowsforms/2008-04/msg00127.html #
// A shortcut to make life easier
private static bool CheckModifier(int vKey)
{
return (KeyboardNativeMethods.GetKeyState(vKey) & 0x8000) > 0;
}
private static Keys AppendModifierStates(Keys keyData)
{
// Is Control being held down?
var control = CheckModifier(KeyboardNativeMethods.VK_CONTROL);
// Is Shift being held down?
var shift = CheckModifier(KeyboardNativeMethods.VK_SHIFT);
// Is Alt being held down?
var alt = CheckModifier(KeyboardNativeMethods.VK_MENU);
// Windows keys
// # combine LWin and RWin key with other keys will potentially corrupt the data
// notable F5 | Keys.LWin == F12, see https://globalmousekeyhook.codeplex.com/workitem/1188
// and the KeyEventArgs.KeyData don't recognize combined data either
// Function (Fn) key
// # CANNOT determine state due to conversion inside keyboard
// See http://en.wikipedia.org/wiki/Fn_key#Technical_details #
return keyData |
(control ? Keys.Control : Keys.None) |
(shift ? Keys.Shift : Keys.None) |
(alt ? Keys.Alt : Keys.None);
}
}
}