RemoteControlDevice.cs
author sl
Sat, 06 Dec 2014 12:13:39 +0100
changeset 24 974f5fdaebfb
parent 23 743cadfacda0
child 25 5f4e0fbb3ea1
permissions -rw-r--r--
Now getting manufacturer and product name.
     1 using System;
     2 using System.Windows.Forms;
     3 using System.Runtime.InteropServices;
     4 using System.Diagnostics;
     5 using System.Text;
     6 using Microsoft.Win32.SafeHandles;
     7 
     8 using Hid.UsageTables;
     9 using Win32;
    10 
    11 
    12 
    13 namespace Devices.RemoteControl
    14 {
    15 
    16 	public enum InputDevice
    17 	{
    18 		Key,
    19 		Mouse,
    20 		OEM
    21 	}
    22 
    23 
    24 	public enum RemoteControlButton
    25 	{
    26 		Clear,
    27 		Down,
    28 		Left,
    29 		Digit0,
    30 		Digit1,
    31 		Digit2,
    32 		Digit3,
    33 		Digit4,
    34 		Digit5,
    35 		Digit6,
    36 		Digit7,
    37 		Digit8,
    38 		Digit9,
    39 		Enter,
    40 		Right,
    41 		Up,
    42 
    43 		Back,
    44 		ChannelDown,
    45 		ChannelUp,
    46 		FastForward,
    47 		VolumeMute,
    48 		Pause,
    49 		Play,
    50         PlayPause,
    51 		Record,
    52 		PreviousTrack,
    53 		Rewind,
    54 		NextTrack,
    55 		Stop,
    56 		VolumeDown,
    57 		VolumeUp,
    58 
    59 		RecordedTV,
    60 		Guide,
    61 		LiveTV,
    62 		MoreInfo,
    63         Print,
    64 		DVDMenu,
    65 		DVDAngle,
    66 		DVDAudio,
    67 		DVDSubtitle,
    68 		MyMusic,
    69 		MyPictures,
    70 		MyVideos,
    71 		MyTV,
    72 		OEM1,
    73 		OEM2,
    74 		StandBy,
    75 		TVJump,
    76 
    77 		Unknown
    78 	}
    79 
    80 
    81 	#region RemoteControlEventArgs
    82 
    83 	public class RemoteControlEventArgs : EventArgs
    84 	{
    85         RemoteControlButton _rcb;
    86 		InputDevice _device;
    87         MceButton iMceButton;
    88         ConsumerControl iConsumerControl;
    89 
    90         public RemoteControlEventArgs(RemoteControlButton rcb, InputDevice device)
    91 		{
    92             SetNullButtons();
    93             //
    94 			_rcb = rcb;
    95 			_device = device;            
    96 		}
    97 
    98         public RemoteControlEventArgs(ConsumerControl aConsumerControl, InputDevice device)
    99         {
   100             SetNullButtons();
   101             //
   102             iConsumerControl = aConsumerControl;            
   103             _device = device;
   104         }
   105 
   106 
   107         public RemoteControlEventArgs(MceButton mce, InputDevice device)
   108         {
   109             SetNullButtons();
   110             //
   111             iMceButton = mce;            
   112             _device = device;
   113         }
   114 
   115         private void SetNullButtons()
   116         {
   117             iConsumerControl = ConsumerControl.Null;
   118             iMceButton = MceButton.Null;
   119             _rcb = RemoteControlButton.Unknown;
   120         }
   121 
   122 		public RemoteControlEventArgs()
   123 		{
   124             iMceButton = MceButton.Null;
   125 			_rcb = RemoteControlButton.Unknown;
   126 			_device = InputDevice.Key;
   127 		}
   128 
   129 		public RemoteControlButton Button
   130 		{
   131 			get { return _rcb;  }
   132 			set { _rcb = value; }
   133 		}
   134 
   135         public MceButton MceButton
   136         {
   137             get { return iMceButton; }
   138             set { iMceButton = value; }
   139         }
   140 
   141         public ConsumerControl ConsumerControl
   142         {
   143             get { return iConsumerControl; }
   144             set { iConsumerControl = value; }
   145         }
   146 
   147 		public InputDevice Device
   148 		{
   149 			get { return _device;  }
   150 			set { _device = value; }
   151 		}
   152 	}
   153 
   154 	#endregion RemoteControlEventArgs
   155 
   156 
   157 	public sealed class RemoteControlDevice
   158 	{
   159 		public delegate bool RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
   160 		public event RemoteControlDeviceEventHandler ButtonPressed;
   161 
   162         /// <summary>
   163         /// Return true if the usage was processed.
   164         /// </summary>
   165         /// <param name="aUsage"></param>
   166         /// <returns></returns>
   167         public delegate bool HidUsageHandler(ushort aUsage);
   168         
   169 
   170 		//-------------------------------------------------------------
   171 		// constructors
   172 		//-------------------------------------------------------------
   173 
   174 		public RemoteControlDevice(IntPtr aHWND)
   175 		{
   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.
   179 
   180 			RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[5];
   181 
   182             int i = 0;
   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;
   187 
   188             i++;
   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;
   193 
   194             i++;
   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;
   199 
   200             i++;
   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;
   205 
   206             i++;
   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;
   211 
   212             //i++;
   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;
   217 
   218 
   219 			if (!Function.RegisterRawInputDevices(rid,(uint) rid.Length,(uint) Marshal.SizeOf(rid[0])))
   220 			{
   221                 throw new ApplicationException("Failed to register raw input devices: " + Marshal.GetLastWin32Error().ToString());
   222 			}
   223 		}
   224 
   225 
   226 		//-------------------------------------------------------------
   227 		// methods
   228 		//-------------------------------------------------------------
   229 
   230 		public void ProcessMessage(Message message)
   231 		{
   232 			switch (message.Msg)
   233 			{
   234 				case Const.WM_KEYDOWN:
   235                     ProcessKeyDown(message.WParam);
   236 					break;
   237                 case Const.WM_INPUT:
   238                     //Returning zero means we processed that message.
   239                     message.Result = new IntPtr(0);
   240 					ProcessInputCommand(ref message);
   241 					break;
   242 			}
   243 
   244 		}
   245 
   246 
   247 		//-------------------------------------------------------------
   248 		// methods (helpers)
   249 		//-------------------------------------------------------------
   250 
   251 		private void ProcessKeyDown(IntPtr wParam)
   252 		{
   253 			RemoteControlButton rcb = RemoteControlButton.Unknown;
   254 
   255             switch (wParam.ToInt32())
   256 			{
   257 				case (int) Keys.Escape:
   258 					rcb = RemoteControlButton.Clear;
   259                     break;
   260                 case (int)Keys.Up:
   261                     rcb = RemoteControlButton.Up;
   262                     break;
   263 				case (int) Keys.Down:
   264 					rcb = RemoteControlButton.Down;
   265 					break;
   266 				case (int) Keys.Left:
   267 					rcb = RemoteControlButton.Left;
   268                     break;
   269                 case (int)Keys.Right:
   270                     rcb = RemoteControlButton.Right;
   271                     break;
   272                 case (int)Keys.Enter:
   273                     rcb = RemoteControlButton.Enter;
   274                     break;
   275 				case (int) Keys.D0:
   276 					rcb = RemoteControlButton.Digit0;
   277 					break;
   278 				case (int) Keys.D1:
   279 					rcb = RemoteControlButton.Digit1;
   280 					break;
   281 				case (int) Keys.D2:
   282 					rcb = RemoteControlButton.Digit2;
   283 					break;
   284 				case (int) Keys.D3:
   285 					rcb = RemoteControlButton.Digit3;
   286 					break;
   287 				case (int) Keys.D4:
   288 					rcb = RemoteControlButton.Digit4;
   289 					break;
   290 				case (int) Keys.D5:
   291 					rcb = RemoteControlButton.Digit5;
   292 					break;
   293 				case (int) Keys.D6:
   294 					rcb = RemoteControlButton.Digit6;
   295 					break;
   296 				case (int) Keys.D7:
   297 					rcb = RemoteControlButton.Digit7;
   298 					break;
   299 				case (int) Keys.D8:
   300 					rcb = RemoteControlButton.Digit8;
   301 					break;
   302 				case (int) Keys.D9:
   303 					rcb = RemoteControlButton.Digit9;
   304                     break;
   305 			}
   306 
   307             if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
   308             {
   309                 Debug.WriteLine("KeyDown: " + rcb.ToString());
   310                 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, InputDevice.Key));
   311             }
   312 		}
   313 
   314 
   315         /// <summary>
   316         /// 
   317         /// </summary>
   318         /// <param name="aUsage"></param>
   319         private bool HidConsumerDeviceHandler(ushort aUsage)
   320         {
   321             if (aUsage == 0)
   322             {
   323                 //Just skip those
   324                 return false;
   325             }
   326 
   327             if (Enum.IsDefined(typeof(ConsumerControl), aUsage) && aUsage != 0) //Our button is a known consumer control
   328             {
   329                 if (this.ButtonPressed != null)
   330                 {
   331                     return this.ButtonPressed(this, new RemoteControlEventArgs((ConsumerControl)aUsage, InputDevice.OEM));
   332                 }
   333                 return false;
   334             }
   335             else
   336             {
   337                 Debug.WriteLine("Unknown Consumer Control!");
   338                 return false;
   339             }
   340         }
   341 
   342         /// <summary>
   343         /// 
   344         /// </summary>
   345         /// <param name="aUsage"></param>
   346         private bool HidMceRemoteHandler(ushort aUsage)
   347         {
   348             if (aUsage == 0)
   349             {
   350                 //Just skip those
   351                 return false;
   352             }
   353 
   354 
   355             if (Enum.IsDefined(typeof(MceButton), aUsage) && aUsage != 0) //Our button is a known MCE button
   356             {
   357                 if (this.ButtonPressed != null)
   358                 {
   359                     return this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)aUsage, InputDevice.OEM));                    
   360                 }
   361                 return false;
   362             }
   363             else
   364             {
   365                 Debug.WriteLine("Unknown MCE button!");
   366                 return false;
   367             }
   368         }
   369 
   370 
   371 		private void ProcessInputCommand(ref Message message)
   372 		{
   373             //We received a WM_INPUT message
   374             Debug.WriteLine("================WM_INPUT================");
   375 
   376             //Check if we received this message while in background or foreground
   377             if (Macro.GET_RAWINPUT_CODE_WPARAM(message.WParam) == Const.RIM_INPUT)
   378             {
   379                 Debug.WriteLine("================FOREGROUND");
   380             }
   381             else if (Macro.GET_RAWINPUT_CODE_WPARAM(message.WParam) == Const.RIM_INPUTSINK)
   382             {
   383                 Debug.WriteLine("================BACKGROUND");
   384             }
   385 
   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;
   390 
   391             try
   392             {
   393                 //Fetch raw input
   394                 RAWINPUT rawInput = new RAWINPUT();
   395                 if (!RawInput.GetRawInputData(message.LParam, ref rawInput, ref rawInputBuffer))
   396                 {
   397                     return;
   398                 }
   399 
   400 
   401 
   402                 //Fetch device info
   403                 RID_DEVICE_INFO deviceInfo = new RID_DEVICE_INFO();
   404                 if (!RawInput.GetDeviceInfo(rawInput.header.hDevice, ref deviceInfo))
   405                 {
   406                     return;
   407                 }
   408 
   409                 //Debug
   410                 string deviceName = RawInput.GetDeviceName(rawInput.header.hDevice);
   411                 Debug.WriteLine("Device name: " + deviceName);
   412                 
   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,
   417                     IntPtr.Zero,
   418                     Win32.CreationDisposition.OPEN_EXISTING,
   419                     Win32.FileFlagsAttributes.FILE_FLAG_OVERLAPPED,
   420                     IntPtr.Zero
   421                     );
   422 
   423                 if (handle.IsInvalid)
   424                 {
   425                     Debug.WriteLine("Failed to CreateFile from device name " + Marshal.GetLastWin32Error().ToString());
   426                 }
   427                 else
   428                 {
   429                     //Get manufacturer string
   430                     StringBuilder manufacturerString = new StringBuilder(256);
   431                     bool returnStatus = Win32.Function.HidD_GetManufacturerString(handle, manufacturerString, manufacturerString.Capacity);
   432                     if (returnStatus)
   433                     {
   434                         Debug.WriteLine("Manufacturer: " + manufacturerString.ToString());
   435                     }
   436 
   437                     //Get product string
   438                     StringBuilder productString = new StringBuilder(256);
   439                     returnStatus = Win32.Function.HidD_GetProductString(handle, productString, productString.Capacity);
   440                     if (returnStatus)
   441                     {
   442                         Debug.WriteLine("Product: " + productString.ToString());
   443                     }
   444 
   445                     handle.Close();
   446 
   447                 }
   448                
   449 
   450 
   451                 if (rawInput.header.dwType == Const.RIM_TYPEHID)  //Check that our raw input is HID                        
   452                 {
   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"));
   456 
   457 
   458                     preParsedData = RawInput.GetPreParsedData(rawInput.header.hDevice);
   459 
   460                     //
   461                     HidUsageHandler usagePageHandler=null;
   462 
   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)
   465                     {                        
   466                         usagePageHandler = HidMceRemoteHandler;
   467                     }
   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)
   470                     {
   471                         usagePageHandler = HidConsumerDeviceHandler;
   472                     }
   473                     //Unknown HID message
   474                     else
   475                     {
   476                         Debug.WriteLine("Unknown HID message.");
   477                         return;
   478                     }
   479 
   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
   482                     {
   483                         return;
   484                     }
   485 
   486 
   487                     //Allocate a buffer for one HID input
   488                     byte[] hidInputReport = new byte[rawInput.hid.dwSizeHid];
   489 
   490                     Debug.WriteLine("Raw input contains " + rawInput.hid.dwCount + " HID input report(s)");
   491 
   492                     //For each HID input report in our raw input
   493                     for (int i = 0; i < rawInput.hid.dwCount; i++)
   494                     {
   495                         //Compute the address from which to copy our HID input
   496                         int hidInputOffset = 0;
   497                         unsafe
   498                         {
   499                             byte* source = (byte*)rawInputBuffer;
   500                             source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + (rawInput.hid.dwSizeHid * i);
   501                             hidInputOffset = (int)source;
   502                         }
   503 
   504                         //Copy HID input into our buffer
   505                         Marshal.Copy(new IntPtr(hidInputOffset), hidInputReport, 0, (int)rawInput.hid.dwSizeHid);
   506 
   507                         //Print HID input report in our debug output
   508                         string hidDump = "HID input report: ";
   509                         foreach (byte b in hidInputReport)
   510                         {
   511                             hidDump += b.ToString("X2");
   512                         }
   513                         Debug.WriteLine(hidDump);
   514                         
   515                         //Proper parsing now
   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)
   520                         {
   521                             Debug.WriteLine("Could not parse HID data!");
   522                         }
   523                         else
   524                         {
   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);
   529                         }
   530                     }
   531 
   532                 }
   533                 else if (rawInput.header.dwType == Const.RIM_TYPEMOUSE)
   534                 {
   535                     Debug.WriteLine("WM_INPUT source device is Mouse.");
   536                     // do mouse handling...
   537                 }
   538                 else if (rawInput.header.dwType == Const.RIM_TYPEKEYBOARD)
   539                 {
   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());
   548                 }
   549             }
   550             finally
   551             {
   552                 //Always executed when leaving our try block
   553                 Marshal.FreeHGlobal(rawInputBuffer);
   554                 Marshal.FreeHGlobal(preParsedData);
   555             }
   556 		}
   557 	}
   558 }