Hid.cs
author StephaneLenclud
Wed, 11 Feb 2015 15:52:53 +0100
changeset 7 5680e56e9b0b
parent 0 316364bd7d25
permissions -rw-r--r--
Left over fixes :)
     1 using Microsoft.Win32.SafeHandles;
     2 using System;
     3 using System.Diagnostics;
     4 using System.IO;
     5 using System.Runtime.InteropServices;
     6 using System.Threading;
     7 using System.Threading.Tasks;
     8 using System.Windows.Forms;
     9 
    10 namespace GenericHid
    11 {
    12 	///  <summary>
    13 	///  Supports Windows API functions for accessing HID-class USB devices.
    14 	///  Includes routines for retrieving information about the configuring a HID and 
    15 	///  sending and receiving reports via control and interrupt transfers. 
    16 	///  </summary>
    17 
    18 	internal sealed partial class Hid
    19 	{
    20 		//  Used in error messages.
    21 
    22 		private const String ModuleName = "Hid";
    23 
    24 		internal NativeMethods.HIDP_CAPS Capabilities;
    25 		internal NativeMethods.HIDD_ATTRIBUTES DeviceAttributes;
    26 
    27 		//  For viewing results of API calls in debug.write statements:
    28 
    29 		internal static Debugging MyDebugging = new Debugging();
    30 
    31 		///  <summary>
    32 		///  Provides a central mechanism for exception handling.
    33 		///  Displays a message box that describes the exception.
    34 		///  </summary>
    35 		///  
    36 		///  <param name="moduleName">  the module where the exception occurred. </param>
    37 		///  <param name="e"> the exception </param>
    38 
    39 		internal static void DisplayException(String moduleName, Exception e)
    40 		{
    41 			//  Create an error message.
    42 
    43 			String message = "Exception: " + e.Message + Environment.NewLine + "Module: " + moduleName + Environment.NewLine + "Method: " + e.TargetSite.Name;
    44 
    45 			const String caption = "Unexpected Exception";
    46 
    47 			MessageBox.Show(message, caption, MessageBoxButtons.OK);
    48 			Debug.Write(message);
    49 
    50 			// Get the last error and display it. 
    51 			Int32 error = Marshal.GetLastWin32Error();
    52 
    53 			Debug.WriteLine("The last Win32 Error was: " + error);
    54 		}
    55 
    56 		///  <summary>
    57 		///  Remove any Input reports waiting in the buffer.
    58 		///  </summary>
    59 		///  <param name="hidHandle"> a handle to a device.   </param>
    60 		///  <returns>
    61 		///  True on success, False on failure.
    62 		///  </returns>
    63 
    64 		internal Boolean FlushQueue(SafeFileHandle hidHandle)
    65 		{
    66 			try
    67 			{
    68 				//  ***
    69 				//  API function: HidD_FlushQueue
    70 
    71 				//  Purpose: Removes any Input reports waiting in the buffer.
    72 
    73 				//  Accepts: a handle to the device.
    74 
    75 				//  Returns: True on success, False on failure.
    76 				//  ***
    77 
    78 				Boolean success = NativeMethods.HidD_FlushQueue(hidHandle);
    79 
    80 				return success;
    81 			}
    82 
    83 			catch (Exception ex)
    84 			{
    85 				DisplayException(ModuleName, ex);
    86 				throw;
    87 			}
    88 		}
    89 
    90 		/// <summary>
    91 		/// Get HID attributes.
    92 		/// </summary>
    93 		/// <param name="hidHandle"> HID handle retrieved with CreateFile </param>
    94 		/// <param name="deviceAttributes"> HID attributes structure </param>
    95 		/// <returns> true on success </returns>
    96 
    97 		internal Boolean GetAttributes(SafeFileHandle hidHandle, ref NativeMethods.HIDD_ATTRIBUTES deviceAttributes)
    98 		{
    99 			Boolean success;
   100 			try
   101 			{
   102 				//  ***
   103 				//  API function:
   104 				//  HidD_GetAttributes
   105 
   106 				//  Purpose:
   107 				//  Retrieves a HIDD_ATTRIBUTES structure containing the Vendor ID, 
   108 				//  Product ID, and Product Version Number for a device.
   109 
   110 				//  Accepts:
   111 				//  A handle returned by CreateFile.
   112 				//  A pointer to receive a HIDD_ATTRIBUTES structure.
   113 
   114 				//  Returns:
   115 				//  True on success, False on failure.
   116 				//  ***                            
   117 
   118 				success = NativeMethods.HidD_GetAttributes(hidHandle, ref deviceAttributes);
   119 			}
   120 
   121 			catch (Exception ex)
   122 			{
   123 				DisplayException(ModuleName, ex);
   124 				throw;
   125 			}
   126 			return success;
   127 		}
   128 
   129 		///  <summary>
   130 		///  Retrieves a structure with information about a device's capabilities. 
   131 		///  </summary>
   132 		///  
   133 		///  <param name="hidHandle"> a handle to a device. </param>
   134 		///  
   135 		///  <returns>
   136 		///  An HIDP_CAPS structure.
   137 		///  </returns>
   138 
   139 		internal NativeMethods.HIDP_CAPS GetDeviceCapabilities(SafeFileHandle hidHandle)
   140 		{
   141 			var preparsedData = new IntPtr();
   142 
   143 			try
   144 			{
   145 				//  ***
   146 				//  API function: HidD_GetPreparsedData
   147 
   148 				//  Purpose: retrieves a pointer to a buffer containing information about the device's capabilities.
   149 				//  HidP_GetCaps and other API functions require a pointer to the buffer.
   150 
   151 				//  Requires: 
   152 				//  A handle returned by CreateFile.
   153 				//  A pointer to a buffer.
   154 
   155 				//  Returns:
   156 				//  True on success, False on failure.
   157 				//  ***
   158 
   159 				NativeMethods.HidD_GetPreparsedData(hidHandle, ref preparsedData);
   160 
   161 				//  ***
   162 				//  API function: HidP_GetCaps
   163 
   164 				//  Purpose: find out a device's capabilities.
   165 				//  For standard devices such as joysticks, you can find out the specific
   166 				//  capabilities of the device.
   167 				//  For a custom device where the software knows what the device is capable of,
   168 				//  this call may be unneeded.
   169 
   170 				//  Accepts:
   171 				//  A pointer returned by HidD_GetPreparsedData
   172 				//  A pointer to a HIDP_CAPS structure.
   173 
   174 				//  Returns: True on success, False on failure.
   175 				//  ***
   176 
   177 				Int32 result = NativeMethods.HidP_GetCaps(preparsedData, ref Capabilities);
   178 				if ((result != 0))
   179 				{
   180 					Debug.WriteLine("");
   181 					Debug.WriteLine("  Usage: " + Convert.ToString(Capabilities.Usage, 16));
   182 					Debug.WriteLine("  Usage Page: " + Convert.ToString(Capabilities.UsagePage, 16));
   183 					Debug.WriteLine("  Input Report Byte Length: " + Capabilities.InputReportByteLength);
   184 					Debug.WriteLine("  Output Report Byte Length: " + Capabilities.OutputReportByteLength);
   185 					Debug.WriteLine("  Feature Report Byte Length: " + Capabilities.FeatureReportByteLength);
   186 					Debug.WriteLine("  Number of Link Collection Nodes: " + Capabilities.NumberLinkCollectionNodes);
   187 					Debug.WriteLine("  Number of Input Button Caps: " + Capabilities.NumberInputButtonCaps);
   188 					Debug.WriteLine("  Number of Input Value Caps: " + Capabilities.NumberInputValueCaps);
   189 					Debug.WriteLine("  Number of Input Data Indices: " + Capabilities.NumberInputDataIndices);
   190 					Debug.WriteLine("  Number of Output Button Caps: " + Capabilities.NumberOutputButtonCaps);
   191 					Debug.WriteLine("  Number of Output Value Caps: " + Capabilities.NumberOutputValueCaps);
   192 					Debug.WriteLine("  Number of Output Data Indices: " + Capabilities.NumberOutputDataIndices);
   193 					Debug.WriteLine("  Number of Feature Button Caps: " + Capabilities.NumberFeatureButtonCaps);
   194 					Debug.WriteLine("  Number of Feature Value Caps: " + Capabilities.NumberFeatureValueCaps);
   195 					Debug.WriteLine("  Number of Feature Data Indices: " + Capabilities.NumberFeatureDataIndices);
   196 
   197 					//  ***
   198 					//  API function: HidP_GetValueCaps
   199 
   200 					//  Purpose: retrieves a buffer containing an array of HidP_ValueCaps structures.
   201 					//  Each structure defines the capabilities of one value.
   202 					//  This application doesn't use this data.
   203 
   204 					//  Accepts:
   205 					//  A report type enumerator from hidpi.h,
   206 					//  A pointer to a buffer for the returned array,
   207 					//  The NumberInputValueCaps member of the device's HidP_Caps structure,
   208 					//  A pointer to the PreparsedData structure returned by HidD_GetPreparsedData.
   209 
   210 					//  Returns: True on success, False on failure.
   211 					//  ***                    
   212 
   213 					Int32 vcSize = Capabilities.NumberInputValueCaps;
   214 					var valueCaps = new Byte[vcSize];
   215 
   216 					NativeMethods.HidP_GetValueCaps(NativeMethods.HidP_Input, valueCaps, ref vcSize, preparsedData);
   217 
   218 					// (To use this data, copy the ValueCaps byte array into an array of structures.)              
   219 				}
   220 			}
   221 
   222 			catch (Exception ex)
   223 			{
   224 				DisplayException(ModuleName, ex);
   225 				throw;
   226 			}
   227 			finally
   228 			{
   229 				//  ***
   230 				//  API function: HidD_FreePreparsedData
   231 
   232 				//  Purpose: frees the buffer reserved by HidD_GetPreparsedData.
   233 
   234 				//  Accepts: A pointer to the PreparsedData structure returned by HidD_GetPreparsedData.
   235 
   236 				//  Returns: True on success, False on failure.
   237 				//  ***
   238 
   239 				if (preparsedData != IntPtr.Zero)
   240 				{
   241 					NativeMethods.HidD_FreePreparsedData(preparsedData);
   242 				}
   243 			}
   244 			return Capabilities;
   245 		}
   246 
   247 		///  <summary>
   248 		///  reads a Feature report from the device.
   249 		///  </summary>
   250 		///  
   251 		///  <param name="hidHandle"> the handle for learning about the device and exchanging Feature reports. </param>	
   252 		///  <param name="inFeatureReportBuffer"> contains the requested report.</param>
   253 
   254 		internal Boolean GetFeatureReport(SafeFileHandle hidHandle, ref Byte[] inFeatureReportBuffer)
   255 		{
   256 			try
   257 			{
   258 				Boolean success = false;
   259 
   260 				//  ***
   261 				//  API function: HidD_GetFeature
   262 				//  Attempts to read a Feature report from the device.
   263 
   264 				//  Requires:
   265 				//  A handle to a HID
   266 				//  A pointer to a buffer containing the report ID and report
   267 				//  The size of the buffer. 
   268 
   269 				//  Returns: true on success, false on failure.
   270 				//  ***                    
   271 
   272 				if (!hidHandle.IsInvalid && !hidHandle.IsClosed)
   273 				{
   274 					success = NativeMethods.HidD_GetFeature(hidHandle, inFeatureReportBuffer, inFeatureReportBuffer.Length);
   275 
   276 					Debug.Print("HidD_GetFeature success = " + success);
   277 				}
   278 				return success;
   279 			}
   280 
   281 			catch (Exception ex)
   282 			{
   283 				DisplayException(ModuleName, ex);
   284 				throw;
   285 			}
   286 		}
   287 
   288 		/// <summary>
   289 		/// Get the HID-class GUID
   290 		/// </summary>
   291 		/// <returns> the GUID </returns>
   292 
   293 		internal Guid GetHidGuid()
   294 		{
   295 			Guid hidGuid = Guid.Empty;
   296 			try
   297 			{
   298 				//  ***
   299 				//  API function: 'HidD_GetHidGuid
   300 
   301 				//  Purpose: Retrieves the interface class GUID for the HID class.
   302 
   303 				//  Accepts: A System.Guid object for storing the GUID.
   304 				//  ***
   305 
   306 				NativeMethods.HidD_GetHidGuid(ref hidGuid);
   307 			}
   308 
   309 			catch (Exception ex)
   310 			{
   311 				DisplayException(ModuleName, ex);
   312 				throw;
   313 			}
   314 			return hidGuid;
   315 		}
   316 
   317 		///  <summary>
   318 		///  Creates a 32-bit Usage from the Usage Page and Usage ID. 
   319 		///  Determines whether the Usage is a system mouse or keyboard.
   320 		///  Can be modified to detect other Usages.
   321 		///  </summary>
   322 		///  
   323 		///  <param name="myCapabilities"> a HIDP_CAPS structure retrieved with HidP_GetCaps. </param>
   324 		///  
   325 		///  <returns>
   326 		///  A String describing the Usage.
   327 		///  </returns>
   328 
   329 		internal String GetHidUsage(NativeMethods.HIDP_CAPS myCapabilities)
   330 		{
   331 			String usageDescription = "";
   332 
   333 			try
   334 			{
   335 				//  Create32-bit Usage from Usage Page and Usage ID.
   336 
   337 				Int32 usage = myCapabilities.UsagePage * 256 + myCapabilities.Usage;
   338 
   339 				if (usage == Convert.ToInt32(0X102))
   340 				{
   341 					usageDescription = "mouse";
   342 				}
   343 				if (usage == Convert.ToInt32(0X106))
   344 				{
   345 					usageDescription = "keyboard";
   346 				}
   347 			}
   348 
   349 			catch (Exception ex)
   350 			{
   351 				DisplayException(ModuleName, ex);
   352 				throw;
   353 			}
   354 
   355 			return usageDescription;
   356 		}
   357 
   358 		///  <summary>
   359 		///  reads an Input report from the device using a control transfer.
   360 		///  </summary>  
   361 		///  <param name="hidHandle"> the handle for learning about the device and exchanging Feature reports. </param>
   362 		/// <param name="inputReportBuffer"> contains the requested report. </param>
   363 
   364 		internal Boolean GetInputReportViaControlTransfer(SafeFileHandle hidHandle, ref Byte[] inputReportBuffer)
   365 		{
   366 			var success = false;
   367 
   368 			try
   369 			{
   370 				//  ***
   371 				//  API function: HidD_GetInputReport
   372 
   373 				//  Purpose: Attempts to read an Input report from the device using a control transfer.
   374 
   375 				//  Requires:
   376 				//  A handle to a HID
   377 				//  A pointer to a buffer containing the report ID and report
   378 				//  The size of the buffer. 
   379 
   380 				//  Returns: true on success, false on failure.
   381 				//  ***
   382 
   383 				if (!hidHandle.IsInvalid && !hidHandle.IsClosed)
   384 				{
   385 					success = NativeMethods.HidD_GetInputReport(hidHandle, inputReportBuffer, inputReportBuffer.Length + 1);
   386 					Debug.Print("HidD_GetInputReport success = " + success);
   387 				}
   388 				return success;
   389 			}
   390 
   391 			catch (Exception ex)
   392 			{
   393 				DisplayException(ModuleName, ex);
   394 				throw;
   395 			}
   396 		}
   397 
   398 		///  <summary>
   399 		///  Reads an Input report from the device using an interrupt transfer.
   400 		///  </summary>
   401 		///  
   402 		///  <param name="deviceData"> the Filestream for writing data. </param> 
   403 		///  <param name="inputReportBuffer"> contains the report ID and report data. </param>
   404 		/// <returns>
   405 		///   True on success. False on failure.
   406 		///  </returns>  
   407 
   408 		internal async Task<Int32> GetInputReportViaInterruptTransfer(FileStream deviceData, Byte[] inputReportBuffer, CancellationTokenSource cts)
   409 		{
   410 			try
   411 			{
   412 				Int32 bytesRead = 0;
   413 				
   414 					// Begin reading an Input report. 
   415 
   416 					Task<Int32> t = deviceData.ReadAsync(inputReportBuffer, 0, inputReportBuffer.Length, cts.Token);
   417 
   418 					bytesRead = await t;
   419 
   420 					// Gets to here only if the read operation completed before a timeout.
   421 
   422 					Debug.Print("Asynchronous read completed. Bytes read = " + Convert.ToString(bytesRead));
   423 
   424 					// The operation has one of these completion states:
   425 
   426 					switch (t.Status)
   427 					{
   428 						case TaskStatus.RanToCompletion:
   429 							Debug.Print("Input report received from device");
   430 							break;
   431 						case TaskStatus.Canceled:
   432 							Debug.Print("Task canceled");
   433 							break;
   434 						case TaskStatus.Faulted:
   435 							Debug.Print("Unhandled exception");
   436 							break;
   437 					}
   438 				return bytesRead;
   439 			}
   440 			catch (Exception ex)
   441 			{
   442 				DisplayException(ModuleName, ex);
   443 				throw;
   444 			}
   445 		}
   446 
   447 		///  <summary>
   448 		///  Retrieves the number of Input reports the HID driver will store.
   449 		///  </summary>
   450 		///  
   451 		///  <param name="hidDeviceObject"> a handle to a device  </param>
   452 		///  <param name="numberOfInputBuffers"> an integer to hold the returned value. </param>
   453 		///  
   454 		///  <returns>
   455 		///  True on success, False on failure.
   456 		///  </returns>
   457 
   458 		internal Boolean GetNumberOfInputBuffers(SafeFileHandle hidDeviceObject, ref Int32 numberOfInputBuffers)
   459 		{
   460 			try
   461 			{
   462 				//  ***
   463 				//  API function: HidD_GetNumInputBuffers
   464 
   465 				//  Purpose: retrieves the number of Input reports the host can store.
   466 				//  Not supported by Windows 98 Gold.
   467 				//  If the buffer is full and another report arrives, the host drops the 
   468 				//  ldest report.
   469 
   470 				//  Accepts: a handle to a device and an integer to hold the number of buffers. 
   471 
   472 				//  Returns: True on success, False on failure.
   473 				//  ***
   474 
   475 				Boolean success = NativeMethods.HidD_GetNumInputBuffers(hidDeviceObject, ref numberOfInputBuffers);
   476 
   477 				return success;
   478 			}
   479 
   480 			catch (Exception ex)
   481 			{
   482 				DisplayException(ModuleName, ex);
   483 				throw;
   484 			}
   485 		}
   486 
   487 		/// <summary>
   488 		/// Timeout if read or write via interrupt transfer doesn't return.
   489 		/// </summary>
   490 
   491 		internal void OnTimeout()
   492 		{
   493 			try
   494 			{
   495 				// No action required.
   496 
   497 				Debug.Print("timeout");
   498 			}
   499 			catch (Exception ex)
   500 			{
   501 				DisplayException(ModuleName, ex);
   502 				throw;
   503 			}
   504 		}
   505 
   506 		/// <summary>
   507 		/// Attempts to open a handle to a HID.
   508 		/// </summary>
   509 		/// <param name="devicePathName"> device path name returned by SetupDiGetDeviceInterfaceDetail </param>
   510 		/// <param name="readAndWrite"> true if requesting read/write access for Input and Output reports </param>
   511 		/// <returns> hidHandle - a handle to the HID </returns>
   512 
   513 		internal SafeFileHandle OpenHandle(String devicePathName, Boolean readAndWrite)
   514 		{
   515 			SafeFileHandle hidHandle;
   516 
   517 			try
   518 			{
   519 				if (readAndWrite)
   520 				{
   521 					//  ***
   522 					//  API function:
   523 					//  CreateFile
   524 
   525 					//  Purpose:
   526 					//  Retrieves a handle to a device.
   527 
   528 					//  Accepts:
   529 					//  A device path name returned by SetupDiGetDeviceInterfaceDetail
   530 					//  The type of access requested (read/write).
   531 					//  FILE_SHARE attributes to allow other processes to access the device while this handle is open.
   532 					//  A Security structure or IntPtr.Zero. 
   533 					//  A creation disposition value. Use OPEN_EXISTING for devices.
   534 					//  Flags and attributes for files. Not used for devices.
   535 					//  Handle to a template file. Not used.
   536 
   537 					//  Returns: a handle without read or write access.
   538 					//  This enables obtaining information about all HIDs, even system
   539 					//  keyboards and mice. 
   540 					//  Separate handles are used for reading and writing.
   541 					//  ***
   542 
   543 					hidHandle = FileIo.CreateFile(devicePathName, FileIo.GenericRead | FileIo.GenericWrite, FileIo.FileShareRead | FileIo.FileShareWrite, IntPtr.Zero, FileIo.OpenExisting, 0, IntPtr.Zero);
   544 				}
   545 				else
   546 				{
   547 					hidHandle = FileIo.CreateFile(devicePathName, 0, FileIo.FileShareRead | FileIo.FileShareWrite, IntPtr.Zero, FileIo.OpenExisting, 0, IntPtr.Zero);
   548 				}
   549 			}
   550 
   551 			catch (Exception ex)
   552 			{
   553 				DisplayException(ModuleName, ex);
   554 				throw;
   555 			}
   556 			return hidHandle;
   557 		}
   558 
   559 		///  <summary>
   560 		///  Writes a Feature report to the device.
   561 		///  </summary>
   562 		///  
   563 		///  <param name="outFeatureReportBuffer"> contains the report ID and report data. </param>
   564 		///  <param name="hidHandle"> handle to the device.  </param>
   565 		///  
   566 		///  <returns>
   567 		///   True on success. False on failure.
   568 		///  </returns>            
   569 
   570 		internal Boolean SendFeatureReport(SafeFileHandle hidHandle, Byte[] outFeatureReportBuffer)
   571 		{
   572 			try
   573 			{
   574 				//  ***
   575 				//  API function: HidD_SetFeature
   576 
   577 				//  Purpose: Attempts to send a Feature report to the device.
   578 
   579 				//  Accepts:
   580 				//  A handle to a HID
   581 				//  A pointer to a buffer containing the report ID and report
   582 				//  The size of the buffer. 
   583 
   584 				//  Returns: true on success, false on failure.
   585 				//  ***
   586 
   587 				Boolean success = NativeMethods.HidD_SetFeature(hidHandle, outFeatureReportBuffer, outFeatureReportBuffer.Length);
   588 
   589 				Debug.Print("HidD_SetFeature success = " + success);
   590 
   591 				return success;
   592 			}
   593 
   594 			catch (Exception ex)
   595 			{
   596 				DisplayException(ModuleName, ex);
   597 				throw;
   598 			}
   599 		}
   600 
   601 		///  <summary>
   602 		///  Writes an Output report to the device using a control transfer.
   603 		///  </summary>
   604 		///  
   605 		///  <param name="outputReportBuffer"> contains the report ID and report data. </param>
   606 		///  <param name="hidHandle"> handle to the device.  </param>
   607 		///  
   608 		///  <returns>
   609 		///   True on success. False on failure.
   610 		///  </returns>            
   611 
   612 		internal Boolean SendOutputReportViaControlTransfer(SafeFileHandle hidHandle, Byte[] outputReportBuffer)
   613 		{
   614 			try
   615 			{
   616 				//  ***
   617 				//  API function: HidD_SetOutputReport
   618 
   619 				//  Purpose: 
   620 				//  Attempts to send an Output report to the device using a control transfer.
   621 
   622 				//  Accepts:
   623 				//  A handle to a HID
   624 				//  A pointer to a buffer containing the report ID and report
   625 				//  The size of the buffer. 
   626 
   627 				//  Returns: true on success, false on failure.
   628 				//  ***                    
   629 
   630 				Boolean success = NativeMethods.HidD_SetOutputReport(hidHandle, outputReportBuffer, outputReportBuffer.Length);
   631 
   632 				Debug.Print("HidD_SetOutputReport success = " + success);
   633 
   634 				return success;
   635 			}
   636 
   637 			catch (Exception ex)
   638 			{
   639 				DisplayException(ModuleName, ex);
   640 				throw;
   641 			}
   642 		}
   643 
   644 		///  <summary>
   645 		///  Writes an Output report to the device using an interrupt transfer.
   646 		///  </summary>
   647 		///  
   648 		///  <param name="fileStreamDeviceData"> the Filestream for writing data. </param> 
   649 		///  <param name="hidHandle"> SafeFileHandle to the device.  </param>
   650 		///  <param name="outputReportBuffer"> contains the report ID and report data. </param>
   651 		///  <param name="cts"> CancellationTokenSource </param>
   652 		///  
   653 		///  <returns>
   654 		///   1 on success. 0 on failure.
   655 		///  </returns>            
   656 
   657 		internal async Task<Boolean> SendOutputReportViaInterruptTransfer
   658 			(FileStream fileStreamDeviceData, SafeFileHandle hidHandle, Byte[] outputReportBuffer, CancellationTokenSource cts)
   659 		{
   660 			try
   661 			{
   662 				var success = false;
   663 				
   664 					// Begin writing the Output report. 
   665 
   666 					Task t = fileStreamDeviceData.WriteAsync(outputReportBuffer, 0, outputReportBuffer.Length, cts.Token);
   667 
   668 					await t;
   669 
   670 					// Gets to here only if the write operation completed before a timeout.
   671 
   672 					Debug.Print("Asynchronous write completed");
   673 
   674 					// The operation has one of these completion states:
   675 
   676 					switch (t.Status)
   677 					{
   678 						case TaskStatus.RanToCompletion:
   679 							success = true;
   680 							Debug.Print("Output report written to device");
   681 							break;
   682 						case TaskStatus.Canceled:
   683 							Debug.Print("Task canceled");
   684 							break;
   685 						case TaskStatus.Faulted:
   686 							Debug.Print("Unhandled exception");
   687 							break;
   688 					}
   689 
   690 				return success;
   691 			}
   692 			catch (Exception ex)
   693 			{
   694 				DisplayException(ModuleName, ex);
   695 				throw;
   696 			}
   697 		}
   698 
   699 		///  <summary>
   700 		///  sets the number of input reports the host HID driver store.
   701 		///  </summary>
   702 		///  
   703 		///  <param name="hidDeviceObject"> a handle to the device.</param>
   704 		///  <param name="numberBuffers"> the requested number of input reports.  </param>
   705 		///  
   706 		///  <returns>
   707 		///  True on success. False on failure.
   708 		///  </returns>
   709 
   710 		internal Boolean SetNumberOfInputBuffers(SafeFileHandle hidDeviceObject, Int32 numberBuffers)
   711 		{
   712 			try
   713 			{
   714 				//  ***
   715 				//  API function: HidD_SetNumInputBuffers
   716 
   717 				//  Purpose: Sets the number of Input reports the host can store.
   718 				//  If the buffer is full and another report arrives, the host drops the 
   719 				//  oldest report.
   720 
   721 				//  Requires:
   722 				//  A handle to a HID
   723 				//  An integer to hold the number of buffers. 
   724 
   725 				//  Returns: true on success, false on failure.
   726 				//  ***
   727 
   728 				NativeMethods.HidD_SetNumInputBuffers(hidDeviceObject, numberBuffers);
   729 				return true;
   730 			}
   731 
   732 			catch (Exception ex)
   733 			{
   734 				DisplayException(ModuleName, ex);
   735 				throw;
   736 			}
   737 		}
   738 	}
   739 }