├── src
├── RawInputProcessor
│ ├── KeyPressState.cs
│ ├── RawDeviceType.cs
│ ├── RawInputCaptureMode.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── RawInputEventArgs.cs
│ ├── RawKeyboardDevice.cs
│ ├── Win32
│ │ ├── RegistryAccess.cs
│ │ ├── Win32Consts.cs
│ │ ├── Enumerations.cs
│ │ ├── Win32Methods.cs
│ │ └── DataStructures.cs
│ ├── RawInput.cs
│ ├── RawPresentationInput.cs
│ ├── RawFormsInput.cs
│ ├── RawInputProcessor.csproj
│ └── RawKeyboard.cs
├── RawInputProcessor.Demo
│ ├── App.xaml.cs
│ ├── App.xaml
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ └── RawInputProcessor.Demo.csproj
└── RawInputProcessor.sln
├── LICENSE.txt
├── README.md
├── .gitattributes
└── .gitignore
/src/RawInputProcessor/KeyPressState.cs:
--------------------------------------------------------------------------------
1 | namespace RawInputProcessor
2 | {
3 | public enum KeyPressState
4 | {
5 | Up,
6 | Down
7 | }
8 | }
--------------------------------------------------------------------------------
/src/RawInputProcessor/RawDeviceType.cs:
--------------------------------------------------------------------------------
1 | namespace RawInputProcessor
2 | {
3 | public enum RawDeviceType
4 | {
5 | Mouse,
6 | Keyboard,
7 | Hid
8 | }
9 | }
--------------------------------------------------------------------------------
/src/RawInputProcessor/RawInputCaptureMode.cs:
--------------------------------------------------------------------------------
1 | namespace RawInputProcessor
2 | {
3 | public enum RawInputCaptureMode
4 | {
5 | Foreground,
6 | ForegroundAndBackground,
7 | }
8 | }
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The Code Project Open License (CPOL)
2 | http://www.codeproject.com/info/cpol10.aspx
3 |
4 | Forked from
5 | http://www.codeproject.com/Articles/17123/Using-Raw-Input-from-C-to-handle-multiple-keyboard
6 |
--------------------------------------------------------------------------------
/src/RawInputProcessor.Demo/App.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace RawInputProcessor.Demo
2 | {
3 | ///
4 | /// Interaction logic for App.xaml
5 | ///
6 | public partial class App
7 | {
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | RawInputProcessor
2 | =================
3 |
4 | Provides a way to distinguish input from multiple keyboards in WPF and Windows Forms.
5 |
6 | Forked from this CodeProject article:
7 | http://www.codeproject.com/Articles/17123/Using-Raw-Input-from-C-to-handle-multiple-keyboard
8 |
--------------------------------------------------------------------------------
/src/RawInputProcessor/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: AssemblyTitle("RawInputProcessor")]
5 | [assembly: AssemblyDescription("")]
6 | [assembly: AssemblyConfiguration("")]
7 | [assembly: AssemblyCompany("")]
8 | [assembly: AssemblyProduct("RawInputProcessor")]
9 | [assembly: AssemblyCopyright("Copyright © 2014")]
10 | [assembly: AssemblyTrademark("")]
11 | [assembly: AssemblyCulture("")]
12 |
13 | [assembly: ComVisible(false)]
14 |
--------------------------------------------------------------------------------
/src/RawInputProcessor/RawInputEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Input;
3 |
4 | namespace RawInputProcessor
5 | {
6 | public sealed class RawInputEventArgs : EventArgs
7 | {
8 | public RawKeyboardDevice Device { get; private set; }
9 | public KeyPressState KeyPressState { get; private set; }
10 | public uint Message { get; private set; }
11 | public Key Key { get; private set; }
12 | public int VirtualKey { get; private set; }
13 | public bool Handled { get; set; }
14 |
15 | internal RawInputEventArgs(RawKeyboardDevice device, KeyPressState keyPressState, uint message, Key key,
16 | int virtualKey)
17 | {
18 | Device = device;
19 | KeyPressState = keyPressState;
20 | Message = message;
21 | Key = key;
22 | VirtualKey = virtualKey;
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/src/RawInputProcessor/RawKeyboardDevice.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RawInputProcessor
4 | {
5 | public sealed class RawKeyboardDevice
6 | {
7 | public string Name { get; private set; }
8 | public RawDeviceType Type { get; private set; }
9 | public IntPtr Handle { get; private set; }
10 | public string Description { get; private set; }
11 |
12 | internal RawKeyboardDevice(string name, RawDeviceType type, IntPtr handle, string description)
13 | {
14 | Handle = handle;
15 | Type = type;
16 | Name = name;
17 | Description = description;
18 | }
19 |
20 | public override string ToString()
21 | {
22 | return string.Format("Device\n Name: {0}\n Type: {1}\n Handle: {2}\n Name: {3}\n",
23 | Name,
24 | Type,
25 | Handle.ToInt64().ToString("X"),
26 | Description);
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/RawInputProcessor.Demo/App.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
10 |
19 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/RawInputProcessor.Demo/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 | using System.Windows;
4 |
5 | [assembly: AssemblyTitle("RawInputProcessor.Demo")]
6 | [assembly: AssemblyDescription("")]
7 | [assembly: AssemblyConfiguration("")]
8 | [assembly: AssemblyCompany("")]
9 | [assembly: AssemblyProduct("RawInputProcessor.Demo")]
10 | [assembly: AssemblyCopyright("Copyright © 2014")]
11 | [assembly: AssemblyTrademark("")]
12 | [assembly: AssemblyCulture("")]
13 |
14 | [assembly: ComVisible(false)]
15 |
16 | [assembly: ThemeInfo(
17 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
18 | //(used if a resource is not found in the page,
19 | // or application resource dictionaries)
20 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
21 | //(used if a resource is not found in the page,
22 | // app, or any theme specific resource dictionaries)
23 | )]
24 |
25 | [assembly: AssemblyVersion("1.0.0.0")]
26 | [assembly: AssemblyFileVersion("1.0.0.0")]
27 |
--------------------------------------------------------------------------------
/src/RawInputProcessor/Win32/RegistryAccess.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32;
2 |
3 | namespace RawInputProcessor.Win32
4 | {
5 | internal static class RegistryAccess
6 | {
7 | private const string Prefix = @"\\?\";
8 |
9 | internal static RegistryKey GetDeviceKey(string device)
10 | {
11 | if (device == null || !device.StartsWith(Prefix)) return null;
12 | string[] array = device.Substring(Prefix.Length).Split('#');
13 | if (array.Length < 3) return null;
14 | return Registry.LocalMachine.OpenSubKey(string.Format(@"System\CurrentControlSet\Enum\{0}\{1}\{2}",
15 | array[0], array[1], array[2]));
16 | }
17 |
18 | internal static string GetClassType(string classGuid)
19 | {
20 | RegistryKey registryKey =
21 | Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Class\\" + classGuid);
22 | if (registryKey == null)
23 | {
24 | return string.Empty;
25 | }
26 | return (string) registryKey.GetValue("Class");
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/RawInputProcessor/RawInput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RawInputProcessor
4 | {
5 | public abstract class RawInput : IDisposable
6 | {
7 | private readonly RawKeyboard _keyboardDriver;
8 |
9 | public event EventHandler KeyPressed
10 | {
11 | add { KeyboardDriver.KeyPressed += value; }
12 | remove { KeyboardDriver.KeyPressed -= value; }
13 | }
14 |
15 | public int NumberOfKeyboards
16 | {
17 | get { return KeyboardDriver.NumberOfKeyboards; }
18 | }
19 |
20 | protected RawKeyboard KeyboardDriver
21 | {
22 | get { return _keyboardDriver; }
23 | }
24 |
25 | protected RawInput(IntPtr handle, RawInputCaptureMode captureMode)
26 | {
27 | _keyboardDriver = new RawKeyboard(handle, captureMode == RawInputCaptureMode.Foreground);
28 | }
29 |
30 | public abstract void AddMessageFilter();
31 | public abstract void RemoveMessageFilter();
32 |
33 | public void Dispose()
34 | {
35 | KeyboardDriver.Dispose();
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/src/RawInputProcessor.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.30723.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RawInputProcessor", "RawInputProcessor\RawInputProcessor.csproj", "{6480E255-35A3-4D61-B48C-F6EFBCAEF891}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RawInputProcessor.Demo", "RawInputProcessor.Demo\RawInputProcessor.Demo.csproj", "{6D678BEC-3454-4ADF-89D9-AAD1ED05C71A}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {6480E255-35A3-4D61-B48C-F6EFBCAEF891}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {6480E255-35A3-4D61-B48C-F6EFBCAEF891}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {6480E255-35A3-4D61-B48C-F6EFBCAEF891}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {6480E255-35A3-4D61-B48C-F6EFBCAEF891}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {6D678BEC-3454-4ADF-89D9-AAD1ED05C71A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {6D678BEC-3454-4ADF-89D9-AAD1ED05C71A}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {6D678BEC-3454-4ADF-89D9-AAD1ED05C71A}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {6D678BEC-3454-4ADF-89D9-AAD1ED05C71A}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/src/RawInputProcessor/Win32/Win32Consts.cs:
--------------------------------------------------------------------------------
1 | namespace RawInputProcessor.Win32
2 | {
3 | internal static class Win32Consts
4 | {
5 | // ReSharper disable InconsistentNaming
6 | internal const int KEYBOARD_OVERRUN_MAKE_CODE = 255;
7 | internal const int WM_APPCOMMAND = 793;
8 | internal const int FAPPCOMMANDMASK = 61440;
9 | internal const int FAPPCOMMANDMOUSE = 32768;
10 | internal const int FAPPCOMMANDOEM = 4096;
11 | internal const int WM_KEYDOWN = 256;
12 | internal const int WM_KEYUP = 257;
13 | internal const int WM_SYSKEYDOWN = 260;
14 | internal const int WM_INPUT = 255;
15 | internal const int WM_USB_DEVICECHANGE = 537;
16 | internal const int WM_INPUT_DEVICE_CHANGE = 254;
17 | internal const int PM_REMOVE = 1;
18 | internal const int VK_SHIFT = 16;
19 | internal const int RI_KEY_MAKE = 0;
20 | internal const int RI_KEY_BREAK = 1;
21 | internal const int RI_KEY_E0 = 2;
22 | internal const int RI_KEY_E1 = 4;
23 | internal const int VK_CONTROL = 17;
24 | internal const int VK_MENU = 18;
25 | internal const int VK_ZOOM = 251;
26 | internal const int VK_LSHIFT = 160;
27 | internal const int VK_RSHIFT = 161;
28 | internal const int VK_LCONTROL = 162;
29 | internal const int VK_RCONTROL = 163;
30 | internal const int VK_LMENU = 164;
31 | internal const int VK_RMENU = 165;
32 | internal const int SC_SHIFT_R = 54;
33 | internal const int SC_SHIFT_L = 42;
34 | internal const int RIM_INPUT = 0;
35 | // ReSharper restore InconsistentNaming
36 | }
37 | }
--------------------------------------------------------------------------------
/src/RawInputProcessor/RawPresentationInput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Interop;
4 | using System.Windows.Media;
5 |
6 | namespace RawInputProcessor
7 | {
8 | public class RawPresentationInput : RawInput
9 | {
10 | private bool _hasFilter;
11 |
12 | public RawPresentationInput(HwndSource hwndSource, RawInputCaptureMode captureMode)
13 | : base(hwndSource.Handle, captureMode)
14 | {
15 | hwndSource.AddHook(Hook);
16 | }
17 |
18 | public RawPresentationInput(Visual visual, RawInputCaptureMode captureMode)
19 | : this(GetHwndSource(visual), captureMode)
20 | {
21 | }
22 |
23 | private static HwndSource GetHwndSource(Visual visual)
24 | {
25 | var source = PresentationSource.FromVisual(visual) as HwndSource;
26 | if (source == null)
27 | {
28 | throw new InvalidOperationException("Cannot find a valid HwndSource");
29 | }
30 | return source;
31 | }
32 |
33 | private IntPtr Hook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
34 | {
35 | KeyboardDriver.HandleMessage(msg, wparam, lparam);
36 | return IntPtr.Zero;
37 | }
38 |
39 | public override void AddMessageFilter()
40 | {
41 | if (_hasFilter)
42 | {
43 | return;
44 | }
45 | ComponentDispatcher.ThreadFilterMessage += OnThreadFilterMessage;
46 | _hasFilter = true;
47 | }
48 |
49 | public override void RemoveMessageFilter()
50 | {
51 | ComponentDispatcher.ThreadFilterMessage -= OnThreadFilterMessage;
52 | _hasFilter = false;
53 | }
54 |
55 | // ReSharper disable once RedundantAssignment
56 | private void OnThreadFilterMessage(ref MSG msg, ref bool handled)
57 | {
58 | handled = KeyboardDriver.HandleMessage(msg.message, msg.wParam, msg.lParam);
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/src/RawInputProcessor.Demo/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
17 |
19 |
20 |
21 |
22 |
24 |
26 |
27 |
28 |
29 |
31 |
32 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/RawInputProcessor.Demo/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Runtime.CompilerServices;
4 |
5 | namespace RawInputProcessor.Demo
6 | {
7 | ///
8 | /// Interaction logic for MainWindow.xaml
9 | ///
10 | public partial class MainWindow : INotifyPropertyChanged
11 | {
12 | private RawPresentationInput _rawInput;
13 | private int _deviceCount;
14 | private RawInputEventArgs _event;
15 |
16 | public MainWindow()
17 | {
18 | DataContext = this;
19 | InitializeComponent();
20 | }
21 |
22 | public int DeviceCount
23 | {
24 | get { return _deviceCount; }
25 | set
26 | {
27 | _deviceCount = value;
28 | OnPropertyChanged();
29 | }
30 | }
31 |
32 | public RawInputEventArgs Event
33 | {
34 | get { return _event; }
35 | set
36 | {
37 | _event = value;
38 | OnPropertyChanged();
39 | }
40 | }
41 |
42 | private void OnKeyPressed(object sender, RawInputEventArgs e)
43 | {
44 | Event = e;
45 | DeviceCount = _rawInput.NumberOfKeyboards;
46 | e.Handled = (ShouldHandle.IsChecked == true);
47 | }
48 |
49 | protected override void OnSourceInitialized(EventArgs e)
50 | {
51 | StartWndProcHandler();
52 | base.OnSourceInitialized(e);
53 | }
54 |
55 | private void StartWndProcHandler()
56 | {
57 | _rawInput = new RawPresentationInput(this, RawInputCaptureMode.Foreground);
58 | _rawInput.KeyPressed += OnKeyPressed;
59 | DeviceCount = _rawInput.NumberOfKeyboards;
60 | }
61 |
62 | public event PropertyChangedEventHandler PropertyChanged;
63 |
64 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
65 | {
66 | PropertyChangedEventHandler propertyChanged = PropertyChanged;
67 | if (propertyChanged != null)
68 | {
69 | propertyChanged(this, new PropertyChangedEventArgs(propertyName));
70 | }
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/src/RawInputProcessor/RawFormsInput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace RawInputProcessor
5 | {
6 | public class RawFormsInput : RawInput
7 | {
8 | // ReSharper disable once NotAccessedField.Local
9 | private RawInputNativeWindow _window;
10 | private PreMessageFilter _filter;
11 |
12 | public override void AddMessageFilter()
13 | {
14 | if (_filter != null)
15 | {
16 | return;
17 | }
18 | _filter = new PreMessageFilter(this);
19 | Application.AddMessageFilter(_filter);
20 | }
21 |
22 | public override void RemoveMessageFilter()
23 | {
24 | if (_filter == null)
25 | {
26 | return;
27 | }
28 | Application.RemoveMessageFilter(_filter);
29 | }
30 |
31 | public RawFormsInput(IntPtr parentHandle, RawInputCaptureMode captureMode)
32 | : base(parentHandle, captureMode)
33 | {
34 | _window = new RawInputNativeWindow(this, parentHandle);
35 | }
36 |
37 | public RawFormsInput(IWin32Window window, RawInputCaptureMode captureMode)
38 | : this(window.Handle, captureMode)
39 | {
40 | }
41 |
42 | private class PreMessageFilter : IMessageFilter
43 | {
44 | private readonly RawFormsInput _rawFormsInput;
45 |
46 | public PreMessageFilter(RawFormsInput rawFormsInput)
47 | {
48 | _rawFormsInput = rawFormsInput;
49 | }
50 |
51 | public bool PreFilterMessage(ref Message m)
52 | {
53 | return _rawFormsInput.KeyboardDriver.HandleMessage(m.Msg, m.WParam, m.LParam);
54 | }
55 | }
56 |
57 | private class RawInputNativeWindow : NativeWindow
58 | {
59 | private readonly RawFormsInput _rawFormsInput;
60 |
61 | public RawInputNativeWindow(RawFormsInput rawFormsInput, IntPtr parentHandle)
62 | {
63 | _rawFormsInput = rawFormsInput;
64 | AssignHandle(parentHandle);
65 | }
66 |
67 | protected override void WndProc(ref Message message)
68 | {
69 | _rawFormsInput.KeyboardDriver.HandleMessage(message.Msg, message.WParam, message.LParam);
70 | base.WndProc(ref message);
71 | }
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/src/RawInputProcessor/RawInputProcessor.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {6480E255-35A3-4D61-B48C-F6EFBCAEF891}
5 | Debug
6 | AnyCPU
7 | Library
8 | RawInputProcessor
9 | v4.5
10 | 4
11 |
12 |
13 | AnyCPU
14 |
15 |
16 | bin\Debug\
17 | true
18 | full
19 | false
20 |
21 |
22 | bin\Release\
23 | true
24 | pdbonly
25 | true
26 |
27 |
28 | RawInputProcessor
29 |
30 |
31 | TRACE;DEBUG
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 | [Dd]ebug/
11 | [Dd]ebugPublic/
12 | [Rr]elease/
13 | [Rr]eleases/
14 | x64/
15 | x86/
16 | build/
17 | bld/
18 | [Bb]in/
19 | [Oo]bj/
20 |
21 | # Roslyn cache directories
22 | *.ide/
23 |
24 | # MSTest test Results
25 | [Tt]est[Rr]esult*/
26 | [Bb]uild[Ll]og.*
27 |
28 | #NUNIT
29 | *.VisualState.xml
30 | TestResult.xml
31 |
32 | # Build Results of an ATL Project
33 | [Dd]ebugPS/
34 | [Rr]eleasePS/
35 | dlldata.c
36 |
37 | *_i.c
38 | *_p.c
39 | *_i.h
40 | *.ilk
41 | *.meta
42 | *.obj
43 | *.pch
44 | *.pdb
45 | *.pgc
46 | *.pgd
47 | *.rsp
48 | *.sbr
49 | *.tlb
50 | *.tli
51 | *.tlh
52 | *.tmp
53 | *.tmp_proj
54 | *.log
55 | *.vspscc
56 | *.vssscc
57 | .builds
58 | *.pidb
59 | *.svclog
60 | *.scc
61 |
62 | # Chutzpah Test files
63 | _Chutzpah*
64 |
65 | # Visual C++ cache files
66 | ipch/
67 | *.aps
68 | *.ncb
69 | *.opensdf
70 | *.sdf
71 | *.cachefile
72 |
73 | # Visual Studio profiler
74 | *.psess
75 | *.vsp
76 | *.vspx
77 |
78 | # TFS 2012 Local Workspace
79 | $tf/
80 |
81 | # Guidance Automation Toolkit
82 | *.gpState
83 |
84 | # ReSharper is a .NET coding add-in
85 | _ReSharper*/
86 | *.[Rr]e[Ss]harper
87 | *.DotSettings.user
88 |
89 | # JustCode is a .NET coding addin-in
90 | .JustCode
91 |
92 | # TeamCity is a build add-in
93 | _TeamCity*
94 |
95 | # DotCover is a Code Coverage Tool
96 | *.dotCover
97 |
98 | # NCrunch
99 | _NCrunch_*
100 | .*crunch*.local.xml
101 |
102 | # MightyMoose
103 | *.mm.*
104 | AutoTest.Net/
105 |
106 | # Web workbench (sass)
107 | .sass-cache/
108 |
109 | # Installshield output folder
110 | [Ee]xpress/
111 |
112 | # DocProject is a documentation generator add-in
113 | DocProject/buildhelp/
114 | DocProject/Help/*.HxT
115 | DocProject/Help/*.HxC
116 | DocProject/Help/*.hhc
117 | DocProject/Help/*.hhk
118 | DocProject/Help/*.hhp
119 | DocProject/Help/Html2
120 | DocProject/Help/html
121 |
122 | # Click-Once directory
123 | publish/
124 |
125 | # Publish Web Output
126 | *.[Pp]ublish.xml
127 | *.azurePubxml
128 | # TODO: Comment the next line if you want to checkin your web deploy settings
129 | # but database connection strings (with potential passwords) will be unencrypted
130 | *.pubxml
131 | *.publishproj
132 |
133 | # NuGet Packages
134 | *.nupkg
135 | # The packages folder can be ignored because of Package Restore
136 | **/packages/*
137 | # except build/, which is used as an MSBuild target.
138 | !**/packages/build/
139 | # If using the old MSBuild-Integrated Package Restore, uncomment this:
140 | #!**/packages/repositories.config
141 |
142 | # Windows Azure Build Output
143 | csx/
144 | *.build.csdef
145 |
146 | # Windows Store app package directory
147 | AppPackages/
148 |
149 | # Others
150 | sql/
151 | *.Cache
152 | ClientBin/
153 | [Ss]tyle[Cc]op.*
154 | ~$*
155 | *~
156 | *.dbmdl
157 | *.dbproj.schemaview
158 | *.pfx
159 | *.publishsettings
160 | node_modules/
161 |
162 | # RIA/Silverlight projects
163 | Generated_Code/
164 |
165 | # Backup & report files from converting an old project file
166 | # to a newer Visual Studio version. Backup files are not needed,
167 | # because we have git ;-)
168 | _UpgradeReport_Files/
169 | Backup*/
170 | UpgradeLog*.XML
171 | UpgradeLog*.htm
172 |
173 | # SQL Server files
174 | *.mdf
175 | *.ldf
176 |
177 | # Business Intelligence projects
178 | *.rdl.data
179 | *.bim.layout
180 | *.bim_*.settings
181 |
182 | # Microsoft Fakes
183 | FakesAssemblies/
184 |
--------------------------------------------------------------------------------
/src/RawInputProcessor.Demo/RawInputProcessor.Demo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {6D678BEC-3454-4ADF-89D9-AAD1ED05C71A}
8 | WinExe
9 | Properties
10 | RawInputProcessor.Demo
11 | RawInputProcessor.Demo
12 | v4.5
13 | 512
14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 4
16 |
17 |
18 | AnyCPU
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | AnyCPU
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 4.0
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | MSBuild:Compile
54 | Designer
55 |
56 |
57 | MSBuild:Compile
58 | Designer
59 |
60 |
61 | App.xaml
62 | Code
63 |
64 |
65 | MainWindow.xaml
66 | Code
67 |
68 |
69 |
70 |
71 | Code
72 |
73 |
74 |
75 |
76 |
77 | {6480E255-35A3-4D61-B48C-F6EFBCAEF891}
78 | RawInputProcessor
79 |
80 |
81 |
82 |
89 |
--------------------------------------------------------------------------------
/src/RawInputProcessor/Win32/Enumerations.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | // ReSharper disable InconsistentNaming
4 | namespace RawInputProcessor.Win32
5 | {
6 | internal enum DataCommand : uint
7 | {
8 | RID_HEADER = 0x10000005, // Get the header information from the RAWINPUT structure.
9 | RID_INPUT = 0x10000003 // Get the raw data from the RAWINPUT structure.
10 | }
11 |
12 | internal static class DeviceType
13 | {
14 | public const int RimTypemouse = 0;
15 | public const int RimTypekeyboard = 1;
16 | public const int RimTypeHid = 2;
17 | }
18 |
19 | internal enum RawInputDeviceInfo : uint
20 | {
21 | RIDI_DEVICENAME = 0x20000007,
22 | RIDI_DEVICEINFO = 0x2000000b,
23 | PREPARSEDDATA = 0x20000005
24 | }
25 |
26 | internal enum BroadcastDeviceType
27 | {
28 | DBT_DEVTYP_OEM = 0,
29 | DBT_DEVTYP_DEVNODE = 1,
30 | DBT_DEVTYP_VOLUME = 2,
31 | DBT_DEVTYP_PORT = 3,
32 | DBT_DEVTYP_NET = 4,
33 | DBT_DEVTYP_DEVICEINTERFACE = 5,
34 | DBT_DEVTYP_HANDLE = 6,
35 | }
36 |
37 | internal enum DeviceNotification
38 | {
39 | /// The hRecipient parameter is a window handle.
40 | DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000,
41 |
42 | /// The hRecipient parameter is a service status handle.
43 | DEVICE_NOTIFY_SERVICE_HANDLE = 0x00000001,
44 |
45 | ///
46 | /// Notifies the recipient of device interface events for all device interface classes. (The dbcc_classguid member is
47 | /// ignored.)
48 | /// This value can be used only if the dbch_devicetype member is DBT_DEVTYP_DEVICEINTERFACE.
49 | ///
50 | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 0x00000004
51 | }
52 |
53 | [Flags]
54 | internal enum RawInputDeviceFlags
55 | {
56 | /// No flags.
57 | NONE = 0,
58 |
59 | ///
60 | /// If set, this removes the top level collection from the inclusion list. This tells the operating system to stop
61 | /// reading from a device which matches the top level collection.
62 | ///
63 | REMOVE = 0x00000001,
64 |
65 | ///
66 | /// If set, this specifies the top level collections to exclude when reading a complete usage page. This flag only
67 | /// affects a TLC whose usage page is already specified with PageOnly.
68 | ///
69 | EXCLUDE = 0x00000010,
70 |
71 | ///
72 | /// If set, this specifies all devices whose top level collection is from the specified UsagePage. Note that Usage
73 | /// must be zero. To exclude a particular top level collection, use Exclude.
74 | ///
75 | PAGEONLY = 0x00000020,
76 |
77 | ///
78 | /// If set, this prevents any devices specified by UsagePage or Usage from generating legacy messages. This is
79 | /// only for the mouse and keyboard.
80 | ///
81 | NOLEGACY = 0x00000030,
82 |
83 | ///
84 | /// If set, this enables the caller to receive the input even when the caller is not in the foreground. Note that
85 | /// WindowHandle must be specified.
86 | ///
87 | INPUTSINK = 0x00000100,
88 |
89 | /// If set, the mouse button click does not activate the other window.
90 | CAPTUREMOUSE = 0x00000200,
91 |
92 | ///
93 | /// If set, the application-defined keyboard device hotkeys are not handled. However, the system hotkeys; for
94 | /// example, ALT+TAB and CTRL+ALT+DEL, are still handled. By default, all keyboard hotkeys are handled. NoHotKeys can
95 | /// be specified even if NoLegacy is not specified and WindowHandle is NULL.
96 | ///
97 | NOHOTKEYS = 0x00000200,
98 |
99 | /// If set, application keys are handled. NoLegacy must be specified. Keyboard only.
100 | APPKEYS = 0x00000400,
101 |
102 | ///
103 | /// If set, this enables the caller to receive input in the background only if the foreground application
104 | /// does not process it. In other words, if the foreground application is not registered for raw input,
105 | /// then the background application that is registered will receive the input.
106 | ///
107 | EXINPUTSINK = 0x00001000,
108 | DEVNOTIFY = 0x00002000
109 | }
110 |
111 | internal enum HidUsagePage : ushort
112 | {
113 | /// Unknown usage page.
114 | UNDEFINED = 0x00,
115 |
116 | /// Generic desktop controls.
117 | GENERIC = 0x01,
118 |
119 | /// Simulation controls.
120 | SIMULATION = 0x02,
121 |
122 | /// Virtual reality controls.
123 | VR = 0x03,
124 |
125 | /// Sports controls.
126 | SPORT = 0x04,
127 |
128 | /// Games controls.
129 | GAME = 0x05,
130 |
131 | /// Keyboard controls.
132 | KEYBOARD = 0x07,
133 | }
134 |
135 | internal enum HidUsage : ushort
136 | {
137 | /// Unknown usage.
138 | Undefined = 0x00,
139 |
140 | /// Pointer
141 | Pointer = 0x01,
142 |
143 | /// Mouse
144 | Mouse = 0x02,
145 |
146 | /// Joystick
147 | Joystick = 0x04,
148 |
149 | /// Game Pad
150 | Gamepad = 0x05,
151 |
152 | /// Keyboard
153 | Keyboard = 0x06,
154 |
155 | /// Keypad
156 | Keypad = 0x07,
157 |
158 | /// Muilt-axis Controller
159 | SystemControl = 0x80,
160 |
161 | /// Tablet PC controls
162 | Tablet = 0x80,
163 |
164 | /// Consumer
165 | Consumer = 0x0C,
166 | }
167 | }
168 | // ReSharper restore InconsistentNaming
169 |
--------------------------------------------------------------------------------
/src/RawInputProcessor/Win32/Win32Methods.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Runtime.InteropServices;
4 | using System.Text;
5 | using System.Windows.Interop;
6 |
7 | namespace RawInputProcessor.Win32
8 | {
9 | internal static class Win32Methods
10 | {
11 | [DllImport("user32")]
12 | [return: MarshalAs(UnmanagedType.Bool)]
13 | public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax,
14 | uint wRemoveMsg);
15 |
16 | [DllImport("user32", SetLastError = true)]
17 | public static extern int GetRawInputData(IntPtr hRawInput, DataCommand command, out InputData buffer,
18 | [In] [Out] ref int size, int cbSizeHeader);
19 |
20 | [DllImport("user32", SetLastError = true)]
21 | public static extern int GetRawInputData(IntPtr hRawInput, DataCommand command, [Out] IntPtr pData,
22 | [In] [Out] ref int size, int sizeHeader);
23 |
24 | [DllImport("user32", SetLastError = true)]
25 | public static extern uint GetRawInputDeviceInfo(IntPtr hDevice, RawInputDeviceInfo command, IntPtr pData,
26 | ref uint size);
27 |
28 | [DllImport("user32")]
29 | public static extern uint GetRawInputDeviceInfo(IntPtr hDevice, uint command, ref DeviceInfo data,
30 | ref uint dataSize);
31 |
32 | [DllImport("user32", SetLastError = true)]
33 | public static extern uint GetRawInputDeviceList(IntPtr pRawInputDeviceList, ref uint numberDevices, uint size);
34 |
35 | [DllImport("user32", SetLastError = true)]
36 | public static extern bool RegisterRawInputDevices(RawInputDevice[] pRawInputDevice, uint numberDevices,
37 | uint size);
38 |
39 | [DllImport("user32", SetLastError = true)]
40 | public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr notificationFilter,
41 | DeviceNotification flags);
42 |
43 | [DllImport("user32", SetLastError = true)]
44 | public static extern bool UnregisterDeviceNotification(IntPtr handle);
45 |
46 | public static int LoWord(int dwValue)
47 | {
48 | return (dwValue & 0xFFFF);
49 | }
50 |
51 | public static int HiWord(Int64 dwValue)
52 | {
53 | return (int)(dwValue >> 16) & ~Win32Consts.FAPPCOMMANDMASK;
54 | }
55 |
56 | public static ushort LowWord(uint val)
57 | {
58 | return (ushort)val;
59 | }
60 |
61 | public static ushort HighWord(uint val)
62 | {
63 | return (ushort)(val >> 16);
64 | }
65 |
66 | public static uint BuildWParam(ushort low, ushort high)
67 | {
68 | return ((uint)high << 16) | low;
69 | }
70 |
71 | public static string GetDeviceDiagnostics()
72 | {
73 | var stringBuilder = new StringBuilder();
74 | uint devices = 0u;
75 | int listSize = Marshal.SizeOf(typeof(RawInputDeviceList));
76 | if (GetRawInputDeviceList(IntPtr.Zero, ref devices, (uint)listSize) != 0u)
77 | {
78 | throw new Win32Exception(Marshal.GetLastWin32Error());
79 | }
80 | var deviceListPtr = Marshal.AllocHGlobal((int)(listSize * devices));
81 | try
82 | {
83 | GetRawInputDeviceList(deviceListPtr, ref devices, (uint)listSize);
84 | int index = 0;
85 | while (index < devices)
86 | {
87 | uint pcbSize = 0u;
88 | var rawInputDeviceList = (RawInputDeviceList)Marshal.PtrToStructure(new IntPtr(deviceListPtr.ToInt64() + listSize * index), typeof(RawInputDeviceList));
89 | GetRawInputDeviceInfo(rawInputDeviceList.hDevice, RawInputDeviceInfo.RIDI_DEVICENAME, IntPtr.Zero, ref pcbSize);
90 | if (pcbSize <= 0u)
91 | {
92 | stringBuilder.AppendLine("pcbSize: " + pcbSize);
93 | stringBuilder.AppendLine(Marshal.GetLastWin32Error().ToString());
94 | string result = stringBuilder.ToString();
95 | return result;
96 | }
97 | var deviceInfoSize = (uint)Marshal.SizeOf(typeof(DeviceInfo));
98 | var deviceInfo = new DeviceInfo
99 | {
100 | Size = Marshal.SizeOf(typeof(DeviceInfo))
101 | };
102 | if (GetRawInputDeviceInfo(rawInputDeviceList.hDevice, 536870923u, ref deviceInfo, ref deviceInfoSize) <= 0u)
103 | {
104 | stringBuilder.AppendLine(Marshal.GetLastWin32Error().ToString());
105 | string result = stringBuilder.ToString();
106 | return result;
107 | }
108 | var deviceInfoPtr = Marshal.AllocHGlobal((int)pcbSize);
109 | try
110 | {
111 | GetRawInputDeviceInfo(rawInputDeviceList.hDevice, RawInputDeviceInfo.RIDI_DEVICENAME, deviceInfoPtr,
112 | ref pcbSize);
113 | string device = Marshal.PtrToStringAnsi(deviceInfoPtr);
114 | if (rawInputDeviceList.dwType == DeviceType.RimTypekeyboard ||
115 | rawInputDeviceList.dwType == DeviceType.RimTypeHid)
116 | {
117 | string deviceDescription = GetDeviceDescription(device);
118 | var rawKeyboardDevice = new RawKeyboardDevice(Marshal.PtrToStringAnsi(deviceInfoPtr),
119 | (RawDeviceType)rawInputDeviceList.dwType, rawInputDeviceList.hDevice, deviceDescription);
120 | stringBuilder.AppendLine(rawKeyboardDevice.ToString());
121 | stringBuilder.AppendLine(deviceInfo.ToString());
122 | stringBuilder.AppendLine(deviceInfo.KeyboardInfo.ToString());
123 | stringBuilder.AppendLine(deviceInfo.HIDInfo.ToString());
124 | }
125 | }
126 | finally
127 | {
128 | Marshal.FreeHGlobal(deviceInfoPtr);
129 | }
130 | index++;
131 | }
132 | }
133 | finally
134 | {
135 | Marshal.FreeHGlobal(deviceListPtr);
136 | }
137 | return stringBuilder.ToString();
138 | }
139 |
140 | public static string GetDeviceDescription(string device)
141 | {
142 | var deviceKey = RegistryAccess.GetDeviceKey(device);
143 | if (deviceKey == null) return string.Empty;
144 |
145 | string text = deviceKey.GetValue("DeviceDesc").ToString();
146 | return text.Substring(text.IndexOf(';') + 1);
147 | }
148 |
149 | public static bool InputInForeground(IntPtr wparam)
150 | {
151 | return wparam.ToInt32() == 0;
152 | }
153 | }
154 | }
--------------------------------------------------------------------------------
/src/RawInputProcessor/Win32/DataStructures.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace RawInputProcessor.Win32
5 | {
6 | [StructLayout(LayoutKind.Explicit)]
7 | internal struct DeviceInfo
8 | {
9 | [FieldOffset(0)] public int Size;
10 | [FieldOffset(4)] public int Type;
11 |
12 | [FieldOffset(8)] public DeviceInfoMouse MouseInfo;
13 | [FieldOffset(8)] public DeviceInfoKeyboard KeyboardInfo;
14 | [FieldOffset(8)] public DeviceInfoHid HIDInfo;
15 |
16 | public override string ToString()
17 | {
18 | return string.Format("DeviceInfo\n Size: {0}\n Type: {1}\n", Size, Type);
19 | }
20 | }
21 |
22 | [StructLayout(LayoutKind.Sequential)]
23 | internal struct DeviceInfoMouse
24 | {
25 | public uint Id; // Identifier of the mouse device
26 | public uint NumberOfButtons; // Number of buttons for the mouse
27 | public uint SampleRate; // Number of data points per second.
28 | public bool HasHorizontalWheel; // True is mouse has wheel for horizontal scrolling else false.
29 |
30 | public override string ToString()
31 | {
32 | return string.Format(
33 | "MouseInfo\n Id: {0}\n NumberOfButtons: {1}\n SampleRate: {2}\n HorizontalWheel: {3}\n", Id,
34 | NumberOfButtons, SampleRate, HasHorizontalWheel);
35 | }
36 | }
37 |
38 | [StructLayout(LayoutKind.Sequential)]
39 | internal struct DeviceInfoKeyboard
40 | {
41 | public uint Type; // Type of the keyboard
42 | public uint SubType; // Subtype of the keyboard
43 | public uint KeyboardMode; // The scan code mode
44 | public uint NumberOfFunctionKeys; // Number of function keys on the keyboard
45 | public uint NumberOfIndicators; // Number of LED indicators on the keyboard
46 | public uint NumberOfKeysTotal; // Total number of keys on the keyboard
47 |
48 | public override string ToString()
49 | {
50 | return
51 | string.Format(
52 | "DeviceInfoKeyboard\n Type: {0}\n SubType: {1}\n KeyboardMode: {2}\n NumberOfFunctionKeys: {3}\n NumberOfIndicators {4}\n NumberOfKeysTotal: {5}\n",
53 | Type, SubType, KeyboardMode, NumberOfFunctionKeys, NumberOfIndicators, NumberOfKeysTotal);
54 | }
55 | }
56 |
57 | [StructLayout(LayoutKind.Sequential)]
58 | internal struct DeviceInfoHid
59 | {
60 | public uint VendorID; // Vendor identifier for the HID
61 | public uint ProductID; // Product identifier for the HID
62 | public uint VersionNumber; // Version number for the device
63 | public ushort UsagePage; // Top-level collection Usage page for the device
64 | public ushort Usage; // Top-level collection Usage for the device
65 |
66 | public override string ToString()
67 | {
68 | return
69 | string.Format(
70 | "HidInfo\n VendorID: {0}\n ProductID: {1}\n VersionNumber: {2}\n UsagePage: {3}\n Usage: {4}\n",
71 | VendorID, ProductID, VersionNumber, UsagePage, Usage);
72 | }
73 | }
74 |
75 | [StructLayout(LayoutKind.Sequential)]
76 | internal struct BroadcastDeviceInterface
77 | {
78 | public Int32 dbcc_size;
79 | public BroadcastDeviceType BroadcastDeviceType;
80 | private readonly Int32 dbcc_reserved;
81 | public Guid dbcc_classguid;
82 | public char dbcc_name;
83 | }
84 |
85 | [StructLayout(LayoutKind.Sequential)]
86 | internal struct RawInputDeviceList
87 | {
88 | public IntPtr hDevice;
89 | public uint dwType;
90 | }
91 |
92 | [StructLayout(LayoutKind.Explicit)]
93 | internal struct RawData
94 | {
95 | [FieldOffset(0)] internal Rawmouse mouse;
96 | [FieldOffset(0)] internal Rawkeyboard keyboard;
97 | [FieldOffset(0)] internal Rawhid hid;
98 | }
99 |
100 | [StructLayout(LayoutKind.Sequential)]
101 | internal struct InputData
102 | {
103 | public RawInputHeader header; // 64 bit header size is 24 32 bit the header size is 16
104 | public RawData data; // Creating the rest in a struct allows the header size to align correctly for 32 or 64 bit
105 | }
106 |
107 | [StructLayout(LayoutKind.Sequential)]
108 | internal struct RawInputHeader
109 | {
110 | public uint dwType; // Type of raw input (RIM_TYPEHID 2, RIM_TYPEKEYBOARD 1, RIM_TYPEMOUSE 0)
111 |
112 | public uint dwSize;
113 | // Size in bytes of the entire input packet of data. This includes RAWINPUT plus possible extra input reports in the RAWHID variable length array.
114 |
115 | public IntPtr hDevice; // A handle to the device generating the raw input data.
116 |
117 | public IntPtr wParam;
118 | // RIM_INPUT 0 if input occurred while application was in the foreground else RIM_INPUTSINK 1 if it was not.
119 |
120 | public override string ToString()
121 | {
122 | return string.Format("RawInputHeader\n dwType : {0}\n dwSize : {1}\n hDevice : {2}\n wParam : {3}", dwType,
123 | dwSize, hDevice, wParam);
124 | }
125 | }
126 |
127 | [StructLayout(LayoutKind.Sequential)]
128 | internal struct Rawhid
129 | {
130 | public uint dwSizHid;
131 | public uint dwCount;
132 | public byte bRawData;
133 |
134 | public override string ToString()
135 | {
136 | return string.Format("Rawhib\n dwSizeHid : {0}\n dwCount : {1}\n bRawData : {2}\n", dwSizHid, dwCount,
137 | bRawData);
138 | }
139 | }
140 |
141 | [StructLayout(LayoutKind.Explicit)]
142 | internal struct Rawmouse
143 | {
144 | [FieldOffset(0)] public ushort usFlags;
145 | [FieldOffset(4)] public uint ulButtons;
146 | [FieldOffset(4)] public ushort usButtonFlags;
147 | [FieldOffset(6)] public ushort usButtonData;
148 | [FieldOffset(8)] public uint ulRawButtons;
149 | [FieldOffset(12)] public int lLastX;
150 | [FieldOffset(16)] public int lLastY;
151 | [FieldOffset(20)] public uint ulExtraInformation;
152 | }
153 |
154 | [StructLayout(LayoutKind.Sequential)]
155 | internal struct Rawkeyboard
156 | {
157 | public ushort Makecode; // Scan code from the key depression
158 | public ushort Flags; // One or more of RI_KEY_MAKE, RI_KEY_BREAK, RI_KEY_E0, RI_KEY_E1
159 | public ushort Reserved; // Always 0
160 | public ushort VKey; // Virtual Key Code
161 | public uint Message; // Corresponding Windows message for exmaple (WM_KEYDOWN, WM_SYASKEYDOWN etc)
162 |
163 | public uint ExtraInformation;
164 | // The device-specific addition information for the event (seems to always be zero for keyboards)
165 |
166 | public override string ToString()
167 | {
168 | return
169 | string.Format(
170 | "Rawkeyboard\n Makecode: {0}\n Makecode(hex) : {0:X}\n Flags: {1}\n Reserved: {2}\n VKeyName: {3}\n Message: {4}\n ExtraInformation {5}\n",
171 | Makecode, Flags, Reserved, VKey, Message, ExtraInformation);
172 | }
173 | }
174 |
175 | [StructLayout(LayoutKind.Sequential)]
176 | internal struct RawInputDevice
177 | {
178 | internal HidUsagePage UsagePage;
179 | internal HidUsage Usage;
180 | internal RawInputDeviceFlags Flags;
181 | internal IntPtr Target;
182 |
183 | public override string ToString()
184 | {
185 | return string.Format("{0}/{1}, flags: {2}, target: {3}", UsagePage, Usage, Flags, Target);
186 | }
187 | }
188 | }
--------------------------------------------------------------------------------
/src/RawInputProcessor/RawKeyboard.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Diagnostics;
5 | using System.Runtime.InteropServices;
6 | using System.Windows.Input;
7 | using System.Windows.Interop;
8 | using RawInputProcessor.Win32;
9 |
10 | namespace RawInputProcessor
11 | {
12 | public sealed class RawKeyboard : IDisposable
13 | {
14 | private static readonly Guid DeviceInterfaceHid = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030");
15 |
16 | private readonly Dictionary _deviceList = new Dictionary();
17 | private readonly object _lock = new object();
18 |
19 | private IntPtr _devNotifyHandle;
20 |
21 | public int NumberOfKeyboards { get; private set; }
22 |
23 | public event EventHandler KeyPressed;
24 |
25 | public RawKeyboard(IntPtr hwnd, bool captureOnlyInForeground)
26 | {
27 | RawInputDevice[] array =
28 | {
29 | new RawInputDevice
30 | {
31 | UsagePage = HidUsagePage.GENERIC,
32 | Usage = HidUsage.Keyboard,
33 | Flags = (captureOnlyInForeground ? RawInputDeviceFlags.NONE : RawInputDeviceFlags.INPUTSINK) | RawInputDeviceFlags.DEVNOTIFY,
34 | Target = hwnd
35 | }
36 | };
37 | if (!Win32Methods.RegisterRawInputDevices(array, (uint)array.Length, (uint)Marshal.SizeOf(array[0])))
38 | {
39 | throw new ApplicationException("Failed to register raw input device(s).", new Win32Exception());
40 | }
41 | EnumerateDevices();
42 | _devNotifyHandle = RegisterForDeviceNotifications(hwnd);
43 | }
44 |
45 | ~RawKeyboard()
46 | {
47 | Dispose();
48 | }
49 |
50 | public void Dispose()
51 | {
52 | GC.SuppressFinalize(this);
53 | if (_devNotifyHandle != IntPtr.Zero)
54 | {
55 | Win32Methods.UnregisterDeviceNotification(_devNotifyHandle);
56 | _devNotifyHandle = IntPtr.Zero;
57 | }
58 | }
59 |
60 | private static IntPtr RegisterForDeviceNotifications(IntPtr parent)
61 | {
62 | IntPtr notifyHandle = IntPtr.Zero;
63 | BroadcastDeviceInterface broadcastDeviceInterface = default(BroadcastDeviceInterface);
64 | broadcastDeviceInterface.dbcc_size = Marshal.SizeOf(broadcastDeviceInterface);
65 | broadcastDeviceInterface.BroadcastDeviceType = BroadcastDeviceType.DBT_DEVTYP_DEVICEINTERFACE;
66 | broadcastDeviceInterface.dbcc_classguid = DeviceInterfaceHid;
67 | IntPtr interfacePtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(BroadcastDeviceInterface)));
68 | try
69 | {
70 | Marshal.StructureToPtr(broadcastDeviceInterface, interfacePtr, false);
71 | notifyHandle = Win32Methods.RegisterDeviceNotification(parent, interfacePtr,
72 | DeviceNotification.DEVICE_NOTIFY_WINDOW_HANDLE);
73 | }
74 | catch (Exception ex)
75 | {
76 | Debug.Print("Registration for device notifications Failed. Error: {0}", Marshal.GetLastWin32Error());
77 | Debug.Print(ex.StackTrace);
78 | }
79 | finally
80 | {
81 | Marshal.FreeHGlobal(interfacePtr);
82 | }
83 |
84 | if (notifyHandle == IntPtr.Zero)
85 | {
86 | Debug.Print("Registration for device notifications Failed. Error: {0}", Marshal.GetLastWin32Error());
87 | }
88 | return notifyHandle;
89 | }
90 |
91 | public void EnumerateDevices()
92 | {
93 | lock (_lock)
94 | {
95 | _deviceList.Clear();
96 | var rawKeyboardDevice = new RawKeyboardDevice("Global Keyboard", RawDeviceType.Keyboard, IntPtr.Zero,
97 | "Fake Keyboard. Some keys (ZOOM, MUTE, VOLUMEUP, VOLUMEDOWN) are sent to rawinput with a handle of zero.");
98 | _deviceList.Add(rawKeyboardDevice.Handle, rawKeyboardDevice);
99 | uint devices = 0u;
100 | int size = Marshal.SizeOf(typeof(RawInputDeviceList));
101 | if (Win32Methods.GetRawInputDeviceList(IntPtr.Zero, ref devices, (uint)size) != 0u)
102 | {
103 | throw new Win32Exception(Marshal.GetLastWin32Error());
104 | }
105 | IntPtr pRawInputDeviceList = Marshal.AllocHGlobal((int)(size * devices));
106 | try
107 | {
108 | Win32Methods.GetRawInputDeviceList(pRawInputDeviceList, ref devices, (uint)size);
109 | int index = 0;
110 | while (index < devices)
111 | {
112 | RawKeyboardDevice device = GetDevice(pRawInputDeviceList, size, index);
113 | if (device != null && !_deviceList.ContainsKey(device.Handle))
114 | {
115 | _deviceList.Add(device.Handle, device);
116 | }
117 | index++;
118 | }
119 | }
120 | finally
121 | {
122 | Marshal.FreeHGlobal(pRawInputDeviceList);
123 | }
124 | NumberOfKeyboards = _deviceList.Count;
125 | }
126 | }
127 |
128 | private static RawKeyboardDevice GetDevice(IntPtr pRawInputDeviceList, int dwSize, int index)
129 | {
130 | uint size = 0u;
131 | // 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
132 | var rawInputDeviceList = (RawInputDeviceList)Marshal.PtrToStructure(new IntPtr(pRawInputDeviceList.ToInt64() + dwSize * index), typeof(RawInputDeviceList));
133 | Win32Methods.GetRawInputDeviceInfo(rawInputDeviceList.hDevice, RawInputDeviceInfo.RIDI_DEVICENAME, IntPtr.Zero, ref size);
134 | if (size <= 0u)
135 | {
136 | return null;
137 | }
138 | IntPtr intPtr = Marshal.AllocHGlobal((int)size);
139 | try
140 | {
141 | Win32Methods.GetRawInputDeviceInfo(rawInputDeviceList.hDevice, RawInputDeviceInfo.RIDI_DEVICENAME, intPtr, ref size);
142 | string device = Marshal.PtrToStringAnsi(intPtr);
143 | if (rawInputDeviceList.dwType == DeviceType.RimTypekeyboard ||
144 | rawInputDeviceList.dwType == DeviceType.RimTypeHid)
145 | {
146 | string deviceDescription = Win32Methods.GetDeviceDescription(device);
147 | return new RawKeyboardDevice(Marshal.PtrToStringAnsi(intPtr),
148 | (RawDeviceType)rawInputDeviceList.dwType, rawInputDeviceList.hDevice, deviceDescription);
149 | }
150 | }
151 | finally
152 | {
153 | if (intPtr != IntPtr.Zero)
154 | {
155 | Marshal.FreeHGlobal(intPtr);
156 | }
157 | }
158 | return null;
159 | }
160 |
161 | private bool ProcessRawInput(IntPtr hdevice)
162 | {
163 | if (_deviceList.Count == 0)
164 | {
165 | return false;
166 | }
167 | int size = 0;
168 | Win32Methods.GetRawInputData(hdevice, DataCommand.RID_INPUT, IntPtr.Zero, ref size, Marshal.SizeOf(typeof(RawInputHeader)));
169 | InputData rawBuffer;
170 | if (Win32Methods.GetRawInputData(hdevice, DataCommand.RID_INPUT, out rawBuffer, ref size, Marshal.SizeOf(typeof(RawInputHeader))) != size)
171 | {
172 | Debug.WriteLine("Error getting the rawinput buffer");
173 | return false;
174 | }
175 | int vKey = rawBuffer.data.keyboard.VKey;
176 | int makecode = rawBuffer.data.keyboard.Makecode;
177 | int flags = rawBuffer.data.keyboard.Flags;
178 | if (vKey == Win32Consts.KEYBOARD_OVERRUN_MAKE_CODE)
179 | {
180 | return false;
181 | }
182 |
183 | RawKeyboardDevice device;
184 | lock (_lock)
185 | {
186 | if (!_deviceList.TryGetValue(rawBuffer.header.hDevice, out device))
187 | {
188 | Debug.WriteLine("Handle: {0} was not in the device list.", rawBuffer.header.hDevice);
189 | return false;
190 | }
191 | }
192 |
193 | var isE0BitSet = ((flags & Win32Consts.RI_KEY_E0) != 0);
194 | bool isBreakBitSet = (flags & Win32Consts.RI_KEY_BREAK) != 0;
195 |
196 | uint message = rawBuffer.data.keyboard.Message;
197 | Key key = KeyInterop.KeyFromVirtualKey(AdjustVirtualKey(rawBuffer, vKey, isE0BitSet, makecode));
198 | EventHandler keyPressed = KeyPressed;
199 | if (keyPressed != null)
200 | {
201 | var rawInputEventArgs = new RawInputEventArgs(device, isBreakBitSet ? KeyPressState.Up : KeyPressState.Down,
202 | message, key, vKey);
203 | keyPressed(this, rawInputEventArgs);
204 | if (rawInputEventArgs.Handled)
205 | {
206 | MSG msg;
207 | Win32Methods.PeekMessage(out msg, IntPtr.Zero, Win32Consts.WM_KEYDOWN, Win32Consts.WM_KEYUP, Win32Consts.PM_REMOVE);
208 | }
209 | return rawInputEventArgs.Handled;
210 | }
211 | return false;
212 | }
213 |
214 | private static int AdjustVirtualKey(InputData rawBuffer, int virtualKey, bool isE0BitSet, int makeCode)
215 | {
216 | var adjustedKey = virtualKey;
217 |
218 | if (rawBuffer.header.hDevice == IntPtr.Zero)
219 | {
220 | // When hDevice is 0 and the vkey is VK_CONTROL indicates the ZOOM key
221 | if (rawBuffer.data.keyboard.VKey == Win32Consts.VK_CONTROL)
222 | {
223 | adjustedKey = Win32Consts.VK_ZOOM;
224 | }
225 | }
226 | else
227 | {
228 | switch (virtualKey)
229 | {
230 | // Right-hand CTRL and ALT have their e0 bit set
231 | case Win32Consts.VK_CONTROL:
232 | adjustedKey = isE0BitSet ? Win32Consts.VK_RCONTROL : Win32Consts.VK_LCONTROL;
233 | break;
234 | case Win32Consts.VK_MENU:
235 | adjustedKey = isE0BitSet ? Win32Consts.VK_RMENU : Win32Consts.VK_LMENU;
236 | break;
237 | case Win32Consts.VK_SHIFT:
238 | adjustedKey = makeCode == Win32Consts.SC_SHIFT_R ? Win32Consts.VK_RSHIFT : Win32Consts.VK_LSHIFT;
239 | break;
240 | default:
241 | adjustedKey = virtualKey;
242 | break;
243 | }
244 | }
245 |
246 | return adjustedKey;
247 | }
248 |
249 | public bool HandleMessage(int msg, IntPtr wparam, IntPtr lparam)
250 | {
251 | switch (msg)
252 | {
253 | case Win32Consts.WM_INPUT_DEVICE_CHANGE:
254 | EnumerateDevices();
255 | break;
256 | case Win32Consts.WM_INPUT:
257 | return ProcessRawInput(lparam);
258 | }
259 | return false;
260 | }
261 |
262 | public static string GetDeviceDianostics()
263 | {
264 | return Win32Methods.GetDeviceDiagnostics();
265 | }
266 | }
267 | }
--------------------------------------------------------------------------------