namespace KeySniffer
{
internal delegate void KeyEventActivated();
internal sealed class KeyboardSnifferService
// Version 2!
{
public const int _NO_KEY = 0;
IntPtr _winhandle;
__SnifferServiceControlServer _sniffer;
public KeyboardSnifferService(Window parent)
{
_winhandle = (new WindowInteropHelper(parent)).Handle;
_sniffer = new __SnifferServiceControlServer(_winhandle);
_sniffer.SetOnKeyEventRaisedCallback(CallbackProc);
}
public KeyboardSnifferService(IntPtr parent)
{
_sniffer = new __SnifferServiceControlServer(_parent);
_sniffer.SetOnKeyEventRaisedCallback(CallbackProc);
}
void CallbackProc(__KeyEventData d)
{
// Mach was...
// Zum Beispiel ein Callback, oder ein Event, oder direkt einen Vorgang laufen lassen...
}
}
internal delegate void __OnKeyEventRaised(__KeyEventData data);
internal class __SnifferServiceControlServer : NativeWindow
{
static __RawKeyboard _keyboardDriver;
readonly IntPtr _devNotifyHandle;
static readonly Guid DeviceInterfaceHid = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030");
__OnKeyEventRaised _callback;
public void SetOnKeyEventRaisedCallback(__OnKeyEventRaised cb)
{
_callback = cb;
}
public __SnifferServiceControlServer(IntPtr parentHandle)
{
AssignHandle(parentHandle);
_keyboardDriver = new __RawKeyboard(parentHandle);
_keyboardDriver.EnumerateDevices();
_devNotifyHandle = RegisterForDeviceNotifications(parentHandle);
_keyboardDriver.KeyPressed += _keyboardDriver_KeyPressed;
}
private void _keyboardDriver_KeyPressed(object sender, __KeyEventData e)
{
if (_callback != null)
_callback(e);
}
static IntPtr RegisterForDeviceNotifications(IntPtr parent)
{
var usbNotifyHandle = IntPtr.Zero;
var bdi = new __BroadcastDeviceInterface();
bdi.dbcc_size = Marshal.SizeOf(bdi);
bdi.BroadcastDeviceType = __BroadcastDeviceType.DBT_DEVTYP_DEVICEINTERFACE;
bdi.dbcc_classguid = DeviceInterfaceHid;
var mem = IntPtr.Zero;
try
{
mem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(__BroadcastDeviceInterface)));
Marshal.StructureToPtr(bdi, mem, false);
usbNotifyHandle = __Win32.RegisterDeviceNotification(parent, mem, __DeviceNotification.DEVICE_NOTIFY_WINDOW_HANDLE);
}
catch (Exception e)
{
}
finally
{
Marshal.FreeHGlobal(mem);
}
if (usbNotifyHandle == IntPtr.Zero)
{
}
return usbNotifyHandle;
}
protected override void WndProc(ref Message message)
{
switch (message.Msg)
{
case __Win32.WM_INPUT:
{
// Should never get here if you are using PreMessageFiltering
_keyboardDriver.ProcessRawInput(message.LParam);
}
break;
case __Win32.WM_USB_DEVICECHANGE:
{
_keyboardDriver.EnumerateDevices();
}
break;
}
if(this.Handle != IntPtr.Zero)
base.WndProc(ref message);
}
~__SnifferServiceControlServer()
{
__Win32.UnregisterDeviceNotification(_devNotifyHandle);
}
}
internal sealed class __RawKeyboard
{
private readonly Dictionary<IntPtr, __KeyEventData> _deviceList = new Dictionary<IntPtr, __KeyEventData>();
public delegate void DeviceEventHandler(object sender, __KeyEventData e);
public event DeviceEventHandler KeyPressed;
readonly object _padLock = new object();
static __InputData _rawBuffer;
public __RawKeyboard(IntPtr hwnd)
{
var rid = new __RawInputDevice[1];
rid[0].UsagePage = __HidUsagePage.GENERIC;
rid[0].Usage = __HidUsage.Keyboard;
rid[0].Flags = __RawInputDeviceFlags.INPUTSINK;
rid[0].Target = hwnd;
if (!__Win32.RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0])))
{
}
}
public void EnumerateDevices()
{
lock (_padLock)
{
_deviceList.Clear();
var globalDevice = new __KeyEventData
{
};
//_deviceList.Add(globalDevice.DeviceHandle, globalDevice);
var numberOfDevices = 0;
uint deviceCount = 0;
var dwSize = (Marshal.SizeOf(typeof(__Rawinputdevicelist)));
if (__Win32.GetRawInputDeviceList(IntPtr.Zero, ref deviceCount, (uint)dwSize) == 0)
{
var pRawInputDeviceList = Marshal.AllocHGlobal((int)(dwSize * deviceCount));
__Win32.GetRawInputDeviceList(pRawInputDeviceList, ref deviceCount, (uint)dwSize);
for (var i = 0; i < deviceCount; i++)
{
uint pcbSize = 0;
// On Window 8 64bit when compiling against .Net > 3.5 using .ToInt32 you will generate an arithmetic overflow. Leave as it is for 32bit/64bit applications
var rid = (__Rawinputdevicelist)Marshal.PtrToStructure(new IntPtr((pRawInputDeviceList.ToInt64() + (dwSize * i))), typeof(__Rawinputdevicelist));
__Win32.GetRawInputDeviceInfo(rid.hDevice, __RawInputDeviceInfo.RIDI_DEVICENAME, IntPtr.Zero, ref pcbSize);
if (pcbSize <= 0) continue;
var pData = Marshal.AllocHGlobal((int)pcbSize);
__Win32.GetRawInputDeviceInfo(rid.hDevice, __RawInputDeviceInfo.RIDI_DEVICENAME, pData, ref pcbSize);
var deviceName = Marshal.PtrToStringAnsi(pData);
if (rid.dwType == __DeviceType.RimTypekeyboard || rid.dwType == __DeviceType.RimTypeHid)
{
var dInfo = new __KeyEventData();
if (!_deviceList.ContainsKey(rid.hDevice))
{
numberOfDevices++;
_deviceList.Add(rid.hDevice, dInfo);
}
}
Marshal.FreeHGlobal(pData);
}
Marshal.FreeHGlobal(pRawInputDeviceList);
return;
}
}
throw new Win32Exception(Marshal.GetLastWin32Error());
}
public bool ProcessRawInput(IntPtr hdevice)
{
if (_deviceList.Count == 0) return false;
var dwSize = 0;
__Win32.GetRawInputData(hdevice, __DataCommand.RID_INPUT, IntPtr.Zero, ref dwSize, Marshal.SizeOf(typeof(__Rawinputheader)));
if (dwSize != __Win32.GetRawInputData(hdevice, __DataCommand.RID_INPUT, out _rawBuffer, ref dwSize, Marshal.SizeOf(typeof(__Rawinputheader))))
{
return false;
}
int virtualKey = _rawBuffer.data.keyboard.VKey;
int makeCode = _rawBuffer.data.keyboard.Makecode;
int flags = _rawBuffer.data.keyboard.Flags;
if (virtualKey == __Win32.KEYBOARD_OVERRUN_MAKE_CODE) return false;
var isE0BitSet = ((flags & __Win32.RI_KEY_E0) != 0);
__KeyEventData keyPressEvent;
if (_deviceList.ContainsKey(_rawBuffer.header.hDevice))
{
lock (_padLock)
{
keyPressEvent = _deviceList[_rawBuffer.header.hDevice];
}
}
else
{
return false;
}
keyPressEvent.Message = _rawBuffer.data.keyboard.Message;
keyPressEvent.VKey = virtualKey;
if (KeyPressed != null)
{
KeyPressed(this, keyPressEvent);
}
return true;
}
}
internal class __KeyEventData
{
public int VKey; // Virtual Key. Corrected for L/R keys(i.e. LSHIFT/RSHIFT) and Zoom
public uint Message; // WM_KEYDOWN or WM_KEYUP
}
[StructLayout(LayoutKind.Explicit)]
internal struct __DeviceInfo
{
[FieldOffset(0)]
public int Size;
[FieldOffset(4)]
public int Type;
}
internal struct __BroadcastDeviceInterface
{
public Int32 dbcc_size;
public __BroadcastDeviceType BroadcastDeviceType;
Int32 dbcc_reserved; // Korrekt!
public Guid dbcc_classguid;
public char dbcc_name; // Korrekt!
}
[StructLayout(LayoutKind.Sequential)]
internal struct __Rawinputdevicelist
{
public IntPtr hDevice;
public uint dwType;
}
[StructLayout(LayoutKind.Explicit)]
internal struct __RawData
{
[FieldOffset(0)]
internal __Rawkeyboard keyboard;
[FieldOffset(0)]
internal __Rawhid hid;
}
[StructLayout(LayoutKind.Sequential)]
internal struct __InputData
{
public __Rawinputheader header; // 64 bit header size is 24 32 bit the header size is 16
public __RawData data; // Creating the rest in a struct allows the header size to align correctly for 32 or 64 bit
}
[StructLayout(LayoutKind.Sequential)]
internal struct __Rawinputheader
{
public uint dwType; // Type of raw input (RIM_TYPEHID 2, RIM_TYPEKEYBOARD 1, RIM_TYPEMOUSE 0)
public uint dwSize; // Size in bytes of the entire input packet of data. This includes RAWINPUT plus possible extra input reports in the RAWHID variable length array.
public IntPtr hDevice; // A handle to the device generating the raw input data.
public IntPtr wParam; // RIM_INPUT 0 if input occurred while application was in the foreground else RIM_INPUTSINK 1 if it was not.
}
[StructLayout(LayoutKind.Sequential)]
internal struct __Rawhid
{
public uint dwSizHid;
public uint dwCount;
public byte bRawData;
}
[StructLayout(LayoutKind.Sequential)]
internal struct __Rawkeyboard
{
public ushort Makecode; // Scan code from the key depression
public ushort Flags; // One or more of RI_KEY_MAKE, RI_KEY_BREAK, RI_KEY_E0, RI_KEY_E1
public ushort Reserved; // Always 0
public ushort VKey; // Virtual Key Code
public uint Message; // Corresponding Windows message for exmaple (WM_KEYDOWN, WM_SYASKEYDOWN etc)
public uint ExtraInformation; // The device-specific addition information for the event (seems to always be zero for keyboards)
}
[StructLayout(LayoutKind.Sequential)]
internal struct __RawInputDevice
{
internal __HidUsagePage UsagePage;
internal __HidUsage Usage;
internal __RawInputDeviceFlags Flags;
internal IntPtr Target;
}
internal enum __DataCommand : uint
{
RID_HEADER = 0x10000005, // Get the header information from the RAWINPUT structure.
RID_INPUT = 0x10000003 // Get the raw data from the RAWINPUT structure.
}
internal static class __DeviceType
{
public const int RimTypemouse = 0;
public const int RimTypekeyboard = 1;
public const int RimTypeHid = 2;
}
internal enum __RawInputDeviceInfo : uint
{
RIDI_DEVICENAME = 0x20000007,
RIDI_DEVICEINFO = 0x2000000b,
PREPARSEDDATA = 0x20000005
}
internal enum __BroadcastDeviceType
{
DBT_DEVTYP_OEM = 0,
DBT_DEVTYP_DEVNODE = 1,
DBT_DEVTYP_VOLUME = 2,
DBT_DEVTYP_PORT = 3,
DBT_DEVTYP_NET = 4,
DBT_DEVTYP_DEVICEINTERFACE = 5,
DBT_DEVTYP_HANDLE = 6,
}
internal enum __DeviceNotification
{
DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000,
DEVICE_NOTIFY_SERVICE_HANDLE = 0x00000001,
DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 0x00000004
}
[Flags]
internal enum __RawInputDeviceFlags
{
NONE = 0,
REMOVE = 0x00000001,
EXCLUDE = 0x00000010,
PAGEONLY = 0x00000020,
NOLEGACY = 0x00000030,
INPUTSINK = 0x00000100,
CAPTUREMOUSE = 0x00000200,
NOHOTKEYS = 0x00000200,
APPKEYS = 0x00000400,
EXINPUTSINK = 0x00001000,
DEVNOTIFY = 0x00002000
}
internal enum __HidUsagePage : ushort
{
UNDEFINED = 0x00,
GENERIC = 0x01,
SIMULATION = 0x02,
VR = 0x03,
SPORT = 0x04,
GAME = 0x05,
KEYBOARD = 0x07,
}
internal enum __HidUsage : ushort
{
Undefined = 0x00,
Pointer = 0x01,
Mouse = 0x02,
Joystick = 0x04,
Gamepad = 0x05,
Keyboard = 0x06,
Keypad = 0x07,
SystemControl = 0x80,
Tablet = 0x80,
Consumer = 0x0C,
}
internal static class __RegistryAccess
{
static internal RegistryKey GetDeviceKey(string device)
{
var split = device.Substring(4).Split('#');
var classCode = split[0]; // ACPI (Class code)
var subClassCode = split[1]; // PNP0303 (SubClass code)
var protocolCode = split[2]; // 3&13c0b0c5&0 (Protocol code)
return Registry.LocalMachine.OpenSubKey(string.Format("System\\CurrentControlSet\\Enum\\{0}\\{1}\\{2}", classCode, subClassCode, protocolCode));
}
static internal string GetClassType(string classGuid)
{
var classGuidKey = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Class\\" + classGuid);
return classGuidKey != null ? (string)classGuidKey.GetValue("Class") : string.Empty;
}
}
internal static class __Win32
{
public const int KEYBOARD_OVERRUN_MAKE_CODE = 0xFF;
public const int WM_KEYDOWN = 0x0100;
public const int WM_KEYUP = 0x0101;
internal const int WM_SYSKEYDOWN = 0x0104;
internal const int WM_SYSKEYUP = 0x0105;
internal const int WM_INPUT = 0x00FF;
internal const int WM_USB_DEVICECHANGE = 0x0219;
internal const int VK_SHIFT = 0x10;
internal const int RI_KEY_MAKE = 0x00; // Key Down
internal const int RI_KEY_BREAK = 0x01; // Key Up
internal const int RI_KEY_E0 = 0x02; // Left version of the key
internal const int RI_KEY_E1 = 0x04; // Right version of the key. Only seems to be set for the Pause/Break key.
internal const int VK_CONTROL = 0x11;
internal const int VK_MENU = 0x12;
internal const int VK_ZOOM = 0xFB;
internal const int VK_LSHIFT = 0xA0;
internal const int VK_RSHIFT = 0xA1;
internal const int VK_LCONTROL = 0xA2;
internal const int VK_RCONTROL = 0xA3;
internal const int VK_LMENU = 0xA4;
internal const int VK_RMENU = 0xA5;
internal const int SC_SHIFT_R = 0x36;
internal const int SC_SHIFT_L = 0x2a;
internal const int RIM_INPUT = 0x00;
[DllImport("User32.dll", SetLastError = true)]
internal static extern int GetRawInputData(IntPtr hRawInput, __DataCommand command, [Out] out __InputData buffer, [In, Out] ref int size, int cbSizeHeader);
[DllImport("User32.dll", SetLastError = true)]
internal static extern int GetRawInputData(IntPtr hRawInput, __DataCommand command, [Out] IntPtr pData, [In, Out] ref int size, int sizeHeader);
[DllImport("User32.dll", SetLastError = true)]
internal static extern uint GetRawInputDeviceInfo(IntPtr hDevice, __RawInputDeviceInfo command, IntPtr pData, ref uint size);
[DllImport("user32.dll")]
public static extern uint GetRawInputDeviceInfo(IntPtr hDevice, uint command, ref __DeviceInfo data, ref uint dataSize);
[DllImport("User32.dll", SetLastError = true)]
internal static extern uint GetRawInputDeviceList(IntPtr pRawInputDeviceList, ref uint numberDevices, uint size);
[DllImport("User32.dll", SetLastError = true)]
internal static extern bool RegisterRawInputDevices(__RawInputDevice[] pRawInputDevice, uint numberDevices, uint size);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr notificationFilter, __DeviceNotification flags);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool UnregisterDeviceNotification(IntPtr handle);
}
}