Now getting manufacturer and product name.
2 using System.Windows.Forms;
3 using System.Runtime.InteropServices;
4 using System.Diagnostics;
6 using Microsoft.Win32.SafeHandles;
13 namespace Devices.RemoteControl
16 public enum InputDevice
24 public enum RemoteControlButton
81 #region RemoteControlEventArgs
83 public class RemoteControlEventArgs : EventArgs
85 RemoteControlButton _rcb;
88 ConsumerControl iConsumerControl;
90 public RemoteControlEventArgs(RemoteControlButton rcb, InputDevice device)
98 public RemoteControlEventArgs(ConsumerControl aConsumerControl, InputDevice device)
102 iConsumerControl = aConsumerControl;
107 public RemoteControlEventArgs(MceButton mce, InputDevice device)
115 private void SetNullButtons()
117 iConsumerControl = ConsumerControl.Null;
118 iMceButton = MceButton.Null;
119 _rcb = RemoteControlButton.Unknown;
122 public RemoteControlEventArgs()
124 iMceButton = MceButton.Null;
125 _rcb = RemoteControlButton.Unknown;
126 _device = InputDevice.Key;
129 public RemoteControlButton Button
132 set { _rcb = value; }
135 public MceButton MceButton
137 get { return iMceButton; }
138 set { iMceButton = value; }
141 public ConsumerControl ConsumerControl
143 get { return iConsumerControl; }
144 set { iConsumerControl = value; }
147 public InputDevice Device
149 get { return _device; }
150 set { _device = value; }
154 #endregion RemoteControlEventArgs
157 public sealed class RemoteControlDevice
159 public delegate bool RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
160 public event RemoteControlDeviceEventHandler ButtonPressed;
163 /// Return true if the usage was processed.
165 /// <param name="aUsage"></param>
166 /// <returns></returns>
167 public delegate bool HidUsageHandler(ushort aUsage);
170 //-------------------------------------------------------------
172 //-------------------------------------------------------------
174 public RemoteControlDevice(IntPtr aHWND)
176 // Register the input device to receive the commands from the
177 // remote device. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/remote_control.asp
178 // for the vendor defined usage page.
180 RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[5];
183 rid[i].usUsagePage = (ushort)Hid.UsagePage.MceRemote;
184 rid[i].usUsage = (ushort)Hid.UsageIdMce.MceRemote;
185 rid[i].dwFlags = Const.RIDEV_EXINPUTSINK;
186 rid[i].hwndTarget = aHWND;
189 rid[i].usUsagePage = (ushort)Hid.UsagePage.Consumer;
190 rid[i].usUsage = (ushort)Hid.UsageIdConsumer.ConsumerControl;
191 rid[i].dwFlags = Const.RIDEV_EXINPUTSINK;
192 rid[i].hwndTarget = aHWND;
195 rid[i].usUsagePage = (ushort)Hid.UsagePage.Consumer;
196 rid[i].usUsage = (ushort)Hid.UsageIdConsumer.Selection;
197 rid[i].dwFlags = Const.RIDEV_EXINPUTSINK;
198 rid[i].hwndTarget = aHWND;
201 rid[i].usUsagePage = (ushort)Hid.UsagePage.GenericDesktopControl;
202 rid[i].usUsage = (ushort)Hid.UsageIdGenericDesktop.SystemControl;
203 rid[i].dwFlags = Const.RIDEV_EXINPUTSINK;
204 rid[i].hwndTarget = aHWND;
207 rid[i].usUsagePage = (ushort)Hid.UsagePage.GenericDesktopControl;
208 rid[i].usUsage = (ushort)Hid.UsageIdGenericDesktop.Keyboard;
209 rid[i].dwFlags = Const.RIDEV_EXINPUTSINK;
210 rid[i].hwndTarget = aHWND;
213 //rid[i].usUsagePage = (ushort)Hid.UsagePage.GenericDesktopControl;
214 //rid[i].usUsage = (ushort)Hid.UsageIdGenericDesktop.Mouse;
215 //rid[i].dwFlags = Const.RIDEV_EXINPUTSINK;
216 //rid[i].hwndTarget = aHWND;
219 if (!Function.RegisterRawInputDevices(rid,(uint) rid.Length,(uint) Marshal.SizeOf(rid[0])))
221 throw new ApplicationException("Failed to register raw input devices: " + Marshal.GetLastWin32Error().ToString());
226 //-------------------------------------------------------------
228 //-------------------------------------------------------------
230 public void ProcessMessage(Message message)
234 case Const.WM_KEYDOWN:
235 ProcessKeyDown(message.WParam);
238 //Returning zero means we processed that message.
239 message.Result = new IntPtr(0);
240 ProcessInputCommand(ref message);
247 //-------------------------------------------------------------
249 //-------------------------------------------------------------
251 private void ProcessKeyDown(IntPtr wParam)
253 RemoteControlButton rcb = RemoteControlButton.Unknown;
255 switch (wParam.ToInt32())
257 case (int) Keys.Escape:
258 rcb = RemoteControlButton.Clear;
261 rcb = RemoteControlButton.Up;
263 case (int) Keys.Down:
264 rcb = RemoteControlButton.Down;
266 case (int) Keys.Left:
267 rcb = RemoteControlButton.Left;
269 case (int)Keys.Right:
270 rcb = RemoteControlButton.Right;
272 case (int)Keys.Enter:
273 rcb = RemoteControlButton.Enter;
276 rcb = RemoteControlButton.Digit0;
279 rcb = RemoteControlButton.Digit1;
282 rcb = RemoteControlButton.Digit2;
285 rcb = RemoteControlButton.Digit3;
288 rcb = RemoteControlButton.Digit4;
291 rcb = RemoteControlButton.Digit5;
294 rcb = RemoteControlButton.Digit6;
297 rcb = RemoteControlButton.Digit7;
300 rcb = RemoteControlButton.Digit8;
303 rcb = RemoteControlButton.Digit9;
307 if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
309 Debug.WriteLine("KeyDown: " + rcb.ToString());
310 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, InputDevice.Key));
318 /// <param name="aUsage"></param>
319 private bool HidConsumerDeviceHandler(ushort aUsage)
327 if (Enum.IsDefined(typeof(ConsumerControl), aUsage) && aUsage != 0) //Our button is a known consumer control
329 if (this.ButtonPressed != null)
331 return this.ButtonPressed(this, new RemoteControlEventArgs((ConsumerControl)aUsage, InputDevice.OEM));
337 Debug.WriteLine("Unknown Consumer Control!");
345 /// <param name="aUsage"></param>
346 private bool HidMceRemoteHandler(ushort aUsage)
355 if (Enum.IsDefined(typeof(MceButton), aUsage) && aUsage != 0) //Our button is a known MCE button
357 if (this.ButtonPressed != null)
359 return this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)aUsage, InputDevice.OEM));
365 Debug.WriteLine("Unknown MCE button!");
371 private void ProcessInputCommand(ref Message message)
373 //We received a WM_INPUT message
374 Debug.WriteLine("================WM_INPUT================");
376 //Check if we received this message while in background or foreground
377 if (Macro.GET_RAWINPUT_CODE_WPARAM(message.WParam) == Const.RIM_INPUT)
379 Debug.WriteLine("================FOREGROUND");
381 else if (Macro.GET_RAWINPUT_CODE_WPARAM(message.WParam) == Const.RIM_INPUTSINK)
383 Debug.WriteLine("================BACKGROUND");
386 //Declare some pointers
387 IntPtr rawInputBuffer = IntPtr.Zero;
388 //My understanding is that this is basically our HID descriptor
389 IntPtr preParsedData = IntPtr.Zero;
394 RAWINPUT rawInput = new RAWINPUT();
395 if (!RawInput.GetRawInputData(message.LParam, ref rawInput, ref rawInputBuffer))
403 RID_DEVICE_INFO deviceInfo = new RID_DEVICE_INFO();
404 if (!RawInput.GetDeviceInfo(rawInput.header.hDevice, ref deviceInfo))
410 string deviceName = RawInput.GetDeviceName(rawInput.header.hDevice);
411 Debug.WriteLine("Device name: " + deviceName);
413 //Open our device from the device name/path
414 SafeFileHandle handle=Win32.Function.CreateFile(deviceName,
415 Win32.FileAccess.NONE,
416 Win32.FileShare.FILE_SHARE_READ|Win32.FileShare.FILE_SHARE_WRITE,
418 Win32.CreationDisposition.OPEN_EXISTING,
419 Win32.FileFlagsAttributes.FILE_FLAG_OVERLAPPED,
423 if (handle.IsInvalid)
425 Debug.WriteLine("Failed to CreateFile from device name " + Marshal.GetLastWin32Error().ToString());
429 //Get manufacturer string
430 StringBuilder manufacturerString = new StringBuilder(256);
431 bool returnStatus = Win32.Function.HidD_GetManufacturerString(handle, manufacturerString, manufacturerString.Capacity);
434 Debug.WriteLine("Manufacturer: " + manufacturerString.ToString());
438 StringBuilder productString = new StringBuilder(256);
439 returnStatus = Win32.Function.HidD_GetProductString(handle, productString, productString.Capacity);
442 Debug.WriteLine("Product: " + productString.ToString());
451 if (rawInput.header.dwType == Const.RIM_TYPEHID) //Check that our raw input is HID
453 Debug.WriteLine("WM_INPUT source device is HID.");
454 //Get Usage Page and Usage
455 Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage ID: 0x" + deviceInfo.hid.usUsage.ToString("X4"));
458 preParsedData = RawInput.GetPreParsedData(rawInput.header.hDevice);
461 HidUsageHandler usagePageHandler=null;
463 //Check if this an MCE remote HID message
464 if (deviceInfo.hid.usUsagePage == (ushort)Hid.UsagePage.MceRemote && deviceInfo.hid.usUsage == (ushort)Hid.UsageIdMce.MceRemote)
466 usagePageHandler = HidMceRemoteHandler;
468 //Check if this is a consumer control HID message
469 else if (deviceInfo.hid.usUsagePage == (ushort)Hid.UsagePage.Consumer && deviceInfo.hid.usUsage == (ushort)Hid.UsageIdConsumer.ConsumerControl)
471 usagePageHandler = HidConsumerDeviceHandler;
473 //Unknown HID message
476 Debug.WriteLine("Unknown HID message.");
480 if (!(rawInput.hid.dwSizeHid > 1 //Make sure our HID msg size more than 1. In fact the first ushort is irrelevant to us for now
481 && rawInput.hid.dwCount > 0)) //Check that we have at least one HID msg
487 //Allocate a buffer for one HID input
488 byte[] hidInputReport = new byte[rawInput.hid.dwSizeHid];
490 Debug.WriteLine("Raw input contains " + rawInput.hid.dwCount + " HID input report(s)");
492 //For each HID input report in our raw input
493 for (int i = 0; i < rawInput.hid.dwCount; i++)
495 //Compute the address from which to copy our HID input
496 int hidInputOffset = 0;
499 byte* source = (byte*)rawInputBuffer;
500 source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + (rawInput.hid.dwSizeHid * i);
501 hidInputOffset = (int)source;
504 //Copy HID input into our buffer
505 Marshal.Copy(new IntPtr(hidInputOffset), hidInputReport, 0, (int)rawInput.hid.dwSizeHid);
507 //Print HID input report in our debug output
508 string hidDump = "HID input report: ";
509 foreach (byte b in hidInputReport)
511 hidDump += b.ToString("X2");
513 Debug.WriteLine(hidDump);
516 uint usageCount = 1; //Assuming a single usage per input report. Is that correct?
517 Win32.USAGE_AND_PAGE[] usages = new Win32.USAGE_AND_PAGE[usageCount];
518 Win32.HidStatus status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, preParsedData, hidInputReport, (uint)hidInputReport.Length);
519 if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS)
521 Debug.WriteLine("Could not parse HID data!");
525 Debug.WriteLine("UsagePage: 0x" + usages[0].UsagePage.ToString("X4"));
526 Debug.WriteLine("Usage: 0x" + usages[0].Usage.ToString("X4"));
527 //Call on our Usage Page handler
528 usagePageHandler(usages[0].Usage);
533 else if (rawInput.header.dwType == Const.RIM_TYPEMOUSE)
535 Debug.WriteLine("WM_INPUT source device is Mouse.");
536 // do mouse handling...
538 else if (rawInput.header.dwType == Const.RIM_TYPEKEYBOARD)
540 Debug.WriteLine("WM_INPUT source device is Keyboard.");
541 // do keyboard handling...
542 Debug.WriteLine("Type: " + deviceInfo.keyboard.dwType.ToString());
543 Debug.WriteLine("SubType: " + deviceInfo.keyboard.dwSubType.ToString());
544 Debug.WriteLine("Mode: " + deviceInfo.keyboard.dwKeyboardMode.ToString());
545 Debug.WriteLine("Number of function keys: " + deviceInfo.keyboard.dwNumberOfFunctionKeys.ToString());
546 Debug.WriteLine("Number of indicators: " + deviceInfo.keyboard.dwNumberOfIndicators.ToString());
547 Debug.WriteLine("Number of keys total: " + deviceInfo.keyboard.dwNumberOfKeysTotal.ToString());
552 //Always executed when leaving our try block
553 Marshal.FreeHGlobal(rawInputBuffer);
554 Marshal.FreeHGlobal(preParsedData);