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