Cleaning and adding GetRawInputDeviceInfo definition.
We will need to use it to get the Usage Page of HID messages.
2 using System.Windows.Forms;
3 using System.Runtime.InteropServices;
4 using System.Diagnostics;
7 namespace BruceThomas.Devices.RemoteControl
9 public enum InputDevice
17 public enum RemoteControlButton
77 Null = 0x00, //Not defined by the specs
79 ClosedCaptioning = 0x2B,
83 TeletextYellow = 0x5D,
119 NetworkSelection = 0x2C,
122 VideoSelection = 0x61
126 #region RemoteControlEventArgs
128 public class RemoteControlEventArgs : EventArgs
130 RemoteControlButton _rcb;
132 MceButton iMceButton;
134 public RemoteControlEventArgs(RemoteControlButton rcb, InputDevice device)
136 iMceButton = MceButton.Null;
141 public RemoteControlEventArgs(MceButton mce, InputDevice device)
144 _rcb = RemoteControlButton.Unknown;
148 public RemoteControlEventArgs()
150 iMceButton = MceButton.Null;
151 _rcb = RemoteControlButton.Unknown;
152 _device = InputDevice.Key;
155 public RemoteControlButton Button
158 set { _rcb = value; }
161 public MceButton MceButton
163 get { return iMceButton; }
164 set { iMceButton = value; }
167 public InputDevice Device
169 get { return _device; }
170 set { _device = value; }
174 #endregion RemoteControlEventArgs
177 public sealed class RemoteControlDevice
180 [StructLayout(LayoutKind.Sequential, Pack = 1)]
181 internal struct RAWINPUTDEVICE
183 [MarshalAs(UnmanagedType.U2)]
184 public ushort usUsagePage;
185 [MarshalAs(UnmanagedType.U2)]
186 public ushort usUsage;
187 [MarshalAs(UnmanagedType.U4)]
189 public IntPtr hwndTarget;
193 [StructLayout(LayoutKind.Sequential, Pack = 1)]
194 internal struct RAWINPUTHEADER
196 [MarshalAs(UnmanagedType.U4)]
198 [MarshalAs(UnmanagedType.U4)]
200 public IntPtr hDevice;
201 [MarshalAs(UnmanagedType.U4)]
206 [StructLayout(LayoutKind.Sequential, Pack = 1)]
207 internal struct RAWHID
209 [MarshalAs(UnmanagedType.U4)]
210 public int dwSizeHid;
211 [MarshalAs(UnmanagedType.U4)]
218 [StructLayout(LayoutKind.Sequential, Pack = 1)]
219 internal struct BUTTONSSTR
221 [MarshalAs(UnmanagedType.U2)]
222 public ushort usButtonFlags;
223 [MarshalAs(UnmanagedType.U2)]
224 public ushort usButtonData;
228 [StructLayout(LayoutKind.Explicit, Pack = 1)]
229 internal struct RAWMOUSE
231 [MarshalAs(UnmanagedType.U2)]
232 [FieldOffset (0)] public ushort usFlags;
233 [MarshalAs(UnmanagedType.U4)]
234 [FieldOffset (4)] public uint ulButtons;
235 [FieldOffset (4)] public BUTTONSSTR buttonsStr;
236 [MarshalAs(UnmanagedType.U4)]
237 [FieldOffset (8)] public uint ulRawButtons;
238 [MarshalAs(UnmanagedType.U4)]
239 [FieldOffset (12)] public int lLastX;
240 [MarshalAs(UnmanagedType.U4)]
241 [FieldOffset (16)] public int lLastY;
242 [MarshalAs(UnmanagedType.U4)]
243 [FieldOffset (20)] public uint ulExtraInformation;
246 [StructLayout(LayoutKind.Sequential, Pack = 1)]
247 internal struct RAWKEYBOARD
249 [MarshalAs(UnmanagedType.U2)]
250 public ushort MakeCode;
251 [MarshalAs(UnmanagedType.U2)]
253 [MarshalAs(UnmanagedType.U2)]
254 public ushort Reserved;
255 [MarshalAs(UnmanagedType.U2)]
257 [MarshalAs(UnmanagedType.U4)]
259 [MarshalAs(UnmanagedType.U4)]
260 public uint ExtraInformation;
264 [StructLayout(LayoutKind.Explicit, Pack=1)]
265 internal struct RAWINPUT
267 [FieldOffset (0)] public RAWINPUTHEADER header;
268 [FieldOffset (16)] public RAWMOUSE mouse;
269 [FieldOffset (16)] public RAWKEYBOARD keyboard;
270 [FieldOffset (16)] public RAWHID hid;
274 [DllImport("User32.dll")]
275 extern static bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevice, uint uiNumDevices, uint cbSize);
277 [DllImport("User32.dll")]
278 extern static uint GetRawInputData(IntPtr hRawInput, uint uiCommand, IntPtr pData, ref uint pcbSize, uint cbSizeHeader);
280 [DllImport("User32.dll")]
281 extern static uint GetRawInputDeviceInfo(IntPtr hDevice, uint uiCommand, IntPtr pData, ref uint pcbSize);
284 private const int WM_KEYDOWN = 0x0100;
285 private const int WM_APPCOMMAND = 0x0319;
286 private const int WM_INPUT = 0x00FF;
288 private const int APPCOMMAND_BROWSER_BACKWARD = 1;
289 private const int APPCOMMAND_VOLUME_MUTE = 8;
290 private const int APPCOMMAND_VOLUME_DOWN = 9;
291 private const int APPCOMMAND_VOLUME_UP = 10;
292 private const int APPCOMMAND_MEDIA_NEXTTRACK = 11;
293 private const int APPCOMMAND_MEDIA_PREVIOUSTRACK = 12;
294 private const int APPCOMMAND_MEDIA_STOP = 13;
295 private const int APPCOMMAND_MEDIA_PLAY_PAUSE = 14;
296 private const int APPCOMMAND_MEDIA_PLAY = 46;
297 private const int APPCOMMAND_MEDIA_PAUSE = 47;
298 private const int APPCOMMAND_MEDIA_RECORD = 48;
299 private const int APPCOMMAND_MEDIA_FAST_FORWARD = 49;
300 private const int APPCOMMAND_MEDIA_REWIND = 50;
301 private const int APPCOMMAND_MEDIA_CHANNEL_UP = 51;
302 private const int APPCOMMAND_MEDIA_CHANNEL_DOWN = 52;
304 private const int RIM_TYPEMOUSE = 0;
305 private const int RIM_TYPEKEYBOARD = 1;
306 private const int RIM_TYPEHID = 2;
308 private const int RID_INPUT = 0x10000003;
309 private const int RID_HEADER = 0x10000005;
311 private const int FAPPCOMMAND_MASK = 0xF000;
312 private const int FAPPCOMMAND_MOUSE = 0x8000;
313 private const int FAPPCOMMAND_KEY = 0;
314 private const int FAPPCOMMAND_OEM = 0x1000;
316 public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
317 public event RemoteControlDeviceEventHandler ButtonPressed;
320 //-------------------------------------------------------------
322 //-------------------------------------------------------------
324 public RemoteControlDevice()
326 // Register the input device to receive the commands from the
327 // remote device. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/remote_control.asp
328 // for the vendor defined usage page.
330 RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[3];
332 rid[0].usUsagePage = 0xFFBC;
333 rid[0].usUsage = 0x88;
336 rid[1].usUsagePage = 0x0C;
337 rid[1].usUsage = 0x01;
340 rid[2].usUsagePage = 0x0C;
341 rid[2].usUsage = 0x80;
344 if (!RegisterRawInputDevices(rid,
346 (uint) Marshal.SizeOf(rid[0]))
349 throw new ApplicationException("Failed to register raw input devices.");
354 //-------------------------------------------------------------
356 //-------------------------------------------------------------
358 public void ProcessMessage(Message message)
365 param = message.WParam.ToInt32();
366 ProcessKeyDown(param);
369 param = message.LParam.ToInt32();
370 ProcessAppCommand(param);
373 ProcessInputCommand(ref message);
374 message.Result = new IntPtr(0);
381 //-------------------------------------------------------------
383 //-------------------------------------------------------------
385 private void ProcessKeyDown(int param)
387 RemoteControlButton rcb = RemoteControlButton.Unknown;
391 case (int) Keys.Escape:
392 rcb = RemoteControlButton.Clear;
394 case (int) Keys.Down:
395 rcb = RemoteControlButton.Down;
397 case (int) Keys.Left:
398 rcb = RemoteControlButton.Left;
401 rcb = RemoteControlButton.Digit0;
404 rcb = RemoteControlButton.Digit1;
407 rcb = RemoteControlButton.Digit2;
410 rcb = RemoteControlButton.Digit3;
413 rcb = RemoteControlButton.Digit4;
416 rcb = RemoteControlButton.Digit5;
419 rcb = RemoteControlButton.Digit6;
422 rcb = RemoteControlButton.Digit7;
425 rcb = RemoteControlButton.Digit8;
428 rcb = RemoteControlButton.Digit9;
430 case (int) Keys.Enter:
431 rcb = RemoteControlButton.Enter;
433 case (int) Keys.Right:
434 rcb = RemoteControlButton.Right;
437 rcb = RemoteControlButton.Up;
441 if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
442 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
446 private void ProcessAppCommand(int param)
448 RemoteControlButton rcb = RemoteControlButton.Unknown;
450 int cmd = (int) (((ushort) (param >> 16)) & ~FAPPCOMMAND_MASK);
454 case APPCOMMAND_BROWSER_BACKWARD:
455 rcb = RemoteControlButton.Back;
457 case APPCOMMAND_MEDIA_CHANNEL_DOWN:
458 rcb = RemoteControlButton.ChannelDown;
460 case APPCOMMAND_MEDIA_CHANNEL_UP:
461 rcb = RemoteControlButton.ChannelUp;
463 case APPCOMMAND_MEDIA_FAST_FORWARD:
464 rcb = RemoteControlButton.FastForward;
466 case APPCOMMAND_VOLUME_MUTE:
467 rcb = RemoteControlButton.VolumeMute;
469 case APPCOMMAND_MEDIA_PAUSE:
470 rcb = RemoteControlButton.Pause;
472 case APPCOMMAND_MEDIA_PLAY:
473 rcb = RemoteControlButton.Play;
475 case APPCOMMAND_MEDIA_PLAY_PAUSE:
476 rcb = RemoteControlButton.PlayPause;
478 case APPCOMMAND_MEDIA_RECORD:
479 rcb = RemoteControlButton.Record;
481 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
482 rcb = RemoteControlButton.PreviousTrack;
484 case APPCOMMAND_MEDIA_REWIND:
485 rcb = RemoteControlButton.Rewind;
487 case APPCOMMAND_MEDIA_NEXTTRACK:
488 rcb = RemoteControlButton.NextTrack;
490 case APPCOMMAND_MEDIA_STOP:
491 rcb = RemoteControlButton.Stop;
493 case APPCOMMAND_VOLUME_DOWN:
494 rcb = RemoteControlButton.VolumeDown;
496 case APPCOMMAND_VOLUME_UP:
497 rcb = RemoteControlButton.VolumeUp;
501 if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
502 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
506 private void ProcessInputCommand(ref Message message)
508 RemoteControlButton rcb = RemoteControlButton.Unknown;
511 uint sizeOfHeader=(uint)Marshal.SizeOf(typeof(RAWINPUTHEADER));
513 //Get the size of our raw input data.
514 GetRawInputData(message.LParam, RID_INPUT, IntPtr.Zero, ref dwSize, sizeOfHeader);
516 //Allocate a large enough buffer
517 IntPtr buffer = Marshal.AllocHGlobal((int) dwSize);
520 if(buffer == IntPtr.Zero)
523 //Now read our RAWINPUT data
524 if (GetRawInputData(message.LParam, RID_INPUT, buffer, ref dwSize, (uint) Marshal.SizeOf(typeof(RAWINPUTHEADER))) != dwSize)
530 RAWINPUT raw = (RAWINPUT)Marshal.PtrToStructure(buffer, typeof(RAWINPUT));
532 //Check that our raw input is HID
533 if (raw.header.dwType == RIM_TYPEHID && raw.hid.dwSizeHid>0)
535 //Allocate a buffer for one HID message
536 byte[] bRawData = new byte[raw.hid.dwSizeHid];
538 //Compute the address from which to copy our HID message
542 byte* source = (byte*)buffer;
543 source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID);
544 pRawData = (int)source;
547 //Copy HID message into our buffer
548 Marshal.Copy(new IntPtr(pRawData), bRawData, 0, raw.hid.dwSizeHid);
549 //bRawData[0] //Not sure what's the meaning of the code at offset 0
550 //TODO: check size before access
551 int rawData = bRawData[1]; //Get button code
552 //Print HID codes in our debug output
553 Debug.WriteLine("HID " + raw.hid.dwCount + "/" + raw.hid.dwSizeHid + ":" + bRawData[0].ToString("X2") + bRawData[1].ToString("X2"));
555 if (Enum.IsDefined(typeof(MceButton), rawData) && rawData!=0) //Our button is a known MCE button
557 if (this.ButtonPressed != null) //What's that?
559 this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)rawData, GetDevice(message.LParam.ToInt32())));
563 else if(raw.header.dwType == RIM_TYPEMOUSE)
565 // do mouse handling...
567 else if(raw.header.dwType == RIM_TYPEKEYBOARD)
569 // do keyboard handling...
574 Marshal.FreeHGlobal(buffer);
579 private InputDevice GetDevice(int param)
581 InputDevice inputDevice;
583 switch ((int) (((ushort) (param >> 16)) & FAPPCOMMAND_MASK))
585 case FAPPCOMMAND_OEM:
586 inputDevice = InputDevice.OEM;
588 case FAPPCOMMAND_MOUSE:
589 inputDevice = InputDevice.Mouse;
592 inputDevice = InputDevice.Key;