Device selection in TreeView now fills in vendor and product ID in our text fields.
1 using Microsoft.Win32.SafeHandles;
3 using System.Diagnostics;
5 using System.Runtime.InteropServices;
6 using System.Threading;
7 using System.Threading.Tasks;
8 using System.Windows.Forms;
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.
18 internal sealed partial class Hid
20 // Used in error messages.
22 private const String ModuleName = "Hid";
24 internal NativeMethods.HIDP_CAPS Capabilities;
25 internal NativeMethods.HIDD_ATTRIBUTES DeviceAttributes;
27 // For viewing results of API calls in debug.write statements:
29 internal static Debugging MyDebugging = new Debugging();
32 /// Provides a central mechanism for exception handling.
33 /// Displays a message box that describes the exception.
36 /// <param name="moduleName"> the module where the exception occurred. </param>
37 /// <param name="e"> the exception </param>
39 internal static void DisplayException(String moduleName, Exception e)
41 // Create an error message.
43 String message = "Exception: " + e.Message + Environment.NewLine + "Module: " + moduleName + Environment.NewLine + "Method: " + e.TargetSite.Name;
45 const String caption = "Unexpected Exception";
47 MessageBox.Show(message, caption, MessageBoxButtons.OK);
50 // Get the last error and display it.
51 Int32 error = Marshal.GetLastWin32Error();
53 Debug.WriteLine("The last Win32 Error was: " + error);
57 /// Remove any Input reports waiting in the buffer.
59 /// <param name="hidHandle"> a handle to a device. </param>
61 /// True on success, False on failure.
64 internal Boolean FlushQueue(SafeFileHandle hidHandle)
69 // API function: HidD_FlushQueue
71 // Purpose: Removes any Input reports waiting in the buffer.
73 // Accepts: a handle to the device.
75 // Returns: True on success, False on failure.
78 Boolean success = NativeMethods.HidD_FlushQueue(hidHandle);
85 DisplayException(ModuleName, ex);
91 /// Get HID attributes.
93 /// <param name="hidHandle"> HID handle retrieved with CreateFile </param>
94 /// <param name="deviceAttributes"> HID attributes structure </param>
95 /// <returns> true on success </returns>
97 internal Boolean GetAttributes(SafeFileHandle hidHandle, ref NativeMethods.HIDD_ATTRIBUTES deviceAttributes)
104 // HidD_GetAttributes
107 // Retrieves a HIDD_ATTRIBUTES structure containing the Vendor ID,
108 // Product ID, and Product Version Number for a device.
111 // A handle returned by CreateFile.
112 // A pointer to receive a HIDD_ATTRIBUTES structure.
115 // True on success, False on failure.
118 success = NativeMethods.HidD_GetAttributes(hidHandle, ref deviceAttributes);
123 DisplayException(ModuleName, ex);
130 /// Retrieves a structure with information about a device's capabilities.
133 /// <param name="hidHandle"> a handle to a device. </param>
136 /// An HIDP_CAPS structure.
139 internal NativeMethods.HIDP_CAPS GetDeviceCapabilities(SafeFileHandle hidHandle)
141 var preparsedData = new IntPtr();
146 // API function: HidD_GetPreparsedData
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.
152 // A handle returned by CreateFile.
153 // A pointer to a buffer.
156 // True on success, False on failure.
159 NativeMethods.HidD_GetPreparsedData(hidHandle, ref preparsedData);
162 // API function: HidP_GetCaps
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.
171 // A pointer returned by HidD_GetPreparsedData
172 // A pointer to a HIDP_CAPS structure.
174 // Returns: True on success, False on failure.
177 Int32 result = NativeMethods.HidP_GetCaps(preparsedData, ref Capabilities);
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);
198 // API function: HidP_GetValueCaps
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.
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.
210 // Returns: True on success, False on failure.
213 Int32 vcSize = Capabilities.NumberInputValueCaps;
214 var valueCaps = new Byte[vcSize];
216 NativeMethods.HidP_GetValueCaps(NativeMethods.HidP_Input, valueCaps, ref vcSize, preparsedData);
218 // (To use this data, copy the ValueCaps byte array into an array of structures.)
224 DisplayException(ModuleName, ex);
230 // API function: HidD_FreePreparsedData
232 // Purpose: frees the buffer reserved by HidD_GetPreparsedData.
234 // Accepts: A pointer to the PreparsedData structure returned by HidD_GetPreparsedData.
236 // Returns: True on success, False on failure.
239 if (preparsedData != IntPtr.Zero)
241 NativeMethods.HidD_FreePreparsedData(preparsedData);
248 /// reads a Feature report from the device.
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>
254 internal Boolean GetFeatureReport(SafeFileHandle hidHandle, ref Byte[] inFeatureReportBuffer)
258 Boolean success = false;
261 // API function: HidD_GetFeature
262 // Attempts to read a Feature report from the device.
266 // A pointer to a buffer containing the report ID and report
267 // The size of the buffer.
269 // Returns: true on success, false on failure.
272 if (!hidHandle.IsInvalid && !hidHandle.IsClosed)
274 success = NativeMethods.HidD_GetFeature(hidHandle, inFeatureReportBuffer, inFeatureReportBuffer.Length);
276 Debug.Print("HidD_GetFeature success = " + success);
283 DisplayException(ModuleName, ex);
289 /// Get the HID-class GUID
291 /// <returns> the GUID </returns>
293 internal Guid GetHidGuid()
295 Guid hidGuid = Guid.Empty;
299 // API function: 'HidD_GetHidGuid
301 // Purpose: Retrieves the interface class GUID for the HID class.
303 // Accepts: A System.Guid object for storing the GUID.
306 NativeMethods.HidD_GetHidGuid(ref hidGuid);
311 DisplayException(ModuleName, ex);
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.
323 /// <param name="myCapabilities"> a HIDP_CAPS structure retrieved with HidP_GetCaps. </param>
326 /// A String describing the Usage.
329 internal String GetHidUsage(NativeMethods.HIDP_CAPS myCapabilities)
331 String usageDescription = "";
335 // Create32-bit Usage from Usage Page and Usage ID.
337 Int32 usage = myCapabilities.UsagePage * 256 + myCapabilities.Usage;
339 if (usage == Convert.ToInt32(0X102))
341 usageDescription = "mouse";
343 if (usage == Convert.ToInt32(0X106))
345 usageDescription = "keyboard";
351 DisplayException(ModuleName, ex);
355 return usageDescription;
359 /// reads an Input report from the device using a control transfer.
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>
364 internal Boolean GetInputReportViaControlTransfer(SafeFileHandle hidHandle, ref Byte[] inputReportBuffer)
371 // API function: HidD_GetInputReport
373 // Purpose: Attempts to read an Input report from the device using a control transfer.
377 // A pointer to a buffer containing the report ID and report
378 // The size of the buffer.
380 // Returns: true on success, false on failure.
383 if (!hidHandle.IsInvalid && !hidHandle.IsClosed)
385 success = NativeMethods.HidD_GetInputReport(hidHandle, inputReportBuffer, inputReportBuffer.Length + 1);
386 Debug.Print("HidD_GetInputReport success = " + success);
393 DisplayException(ModuleName, ex);
399 /// Reads an Input report from the device using an interrupt transfer.
402 /// <param name="deviceData"> the Filestream for writing data. </param>
403 /// <param name="inputReportBuffer"> contains the report ID and report data. </param>
405 /// True on success. False on failure.
408 internal async Task<Int32> GetInputReportViaInterruptTransfer(FileStream deviceData, Byte[] inputReportBuffer, CancellationTokenSource cts)
414 // Begin reading an Input report.
416 Task<Int32> t = deviceData.ReadAsync(inputReportBuffer, 0, inputReportBuffer.Length, cts.Token);
420 // Gets to here only if the read operation completed before a timeout.
422 Debug.Print("Asynchronous read completed. Bytes read = " + Convert.ToString(bytesRead));
424 // The operation has one of these completion states:
428 case TaskStatus.RanToCompletion:
429 Debug.Print("Input report received from device");
431 case TaskStatus.Canceled:
432 Debug.Print("Task canceled");
434 case TaskStatus.Faulted:
435 Debug.Print("Unhandled exception");
442 DisplayException(ModuleName, ex);
448 /// Retrieves the number of Input reports the HID driver will store.
451 /// <param name="hidDeviceObject"> a handle to a device </param>
452 /// <param name="numberOfInputBuffers"> an integer to hold the returned value. </param>
455 /// True on success, False on failure.
458 internal Boolean GetNumberOfInputBuffers(SafeFileHandle hidDeviceObject, ref Int32 numberOfInputBuffers)
463 // API function: HidD_GetNumInputBuffers
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
470 // Accepts: a handle to a device and an integer to hold the number of buffers.
472 // Returns: True on success, False on failure.
475 Boolean success = NativeMethods.HidD_GetNumInputBuffers(hidDeviceObject, ref numberOfInputBuffers);
482 DisplayException(ModuleName, ex);
488 /// Timeout if read or write via interrupt transfer doesn't return.
491 internal void OnTimeout()
495 // No action required.
497 Debug.Print("timeout");
501 DisplayException(ModuleName, ex);
507 /// Attempts to open a handle to a HID.
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>
513 internal SafeFileHandle OpenHandle(String devicePathName, Boolean readAndWrite)
515 SafeFileHandle hidHandle;
526 // Retrieves a handle to a device.
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.
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.
543 hidHandle = FileIo.CreateFile(devicePathName, FileIo.GenericRead | FileIo.GenericWrite, FileIo.FileShareRead | FileIo.FileShareWrite, IntPtr.Zero, FileIo.OpenExisting, 0, IntPtr.Zero);
547 hidHandle = FileIo.CreateFile(devicePathName, 0, FileIo.FileShareRead | FileIo.FileShareWrite, IntPtr.Zero, FileIo.OpenExisting, 0, IntPtr.Zero);
553 DisplayException(ModuleName, ex);
560 /// Writes a Feature report to the device.
563 /// <param name="outFeatureReportBuffer"> contains the report ID and report data. </param>
564 /// <param name="hidHandle"> handle to the device. </param>
567 /// True on success. False on failure.
570 internal Boolean SendFeatureReport(SafeFileHandle hidHandle, Byte[] outFeatureReportBuffer)
575 // API function: HidD_SetFeature
577 // Purpose: Attempts to send a Feature report to the device.
581 // A pointer to a buffer containing the report ID and report
582 // The size of the buffer.
584 // Returns: true on success, false on failure.
587 Boolean success = NativeMethods.HidD_SetFeature(hidHandle, outFeatureReportBuffer, outFeatureReportBuffer.Length);
589 Debug.Print("HidD_SetFeature success = " + success);
596 DisplayException(ModuleName, ex);
602 /// Writes an Output report to the device using a control transfer.
605 /// <param name="outputReportBuffer"> contains the report ID and report data. </param>
606 /// <param name="hidHandle"> handle to the device. </param>
609 /// True on success. False on failure.
612 internal Boolean SendOutputReportViaControlTransfer(SafeFileHandle hidHandle, Byte[] outputReportBuffer)
617 // API function: HidD_SetOutputReport
620 // Attempts to send an Output report to the device using a control transfer.
624 // A pointer to a buffer containing the report ID and report
625 // The size of the buffer.
627 // Returns: true on success, false on failure.
630 Boolean success = NativeMethods.HidD_SetOutputReport(hidHandle, outputReportBuffer, outputReportBuffer.Length + 1);
632 Debug.Print("HidD_SetOutputReport success = " + success);
639 DisplayException(ModuleName, ex);
645 /// Writes an Output report to the device using an interrupt transfer.
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>
654 /// 1 on success. 0 on failure.
657 internal async Task<Boolean> SendOutputReportViaInterruptTransfer
658 (FileStream fileStreamDeviceData, SafeFileHandle hidHandle, Byte[] outputReportBuffer, CancellationTokenSource cts)
664 // Begin writing the Output report.
666 Task t = fileStreamDeviceData.WriteAsync(outputReportBuffer, 0, outputReportBuffer.Length, cts.Token);
670 // Gets to here only if the write operation completed before a timeout.
672 Debug.Print("Asynchronous write completed");
674 // The operation has one of these completion states:
678 case TaskStatus.RanToCompletion:
680 Debug.Print("Output report written to device");
682 case TaskStatus.Canceled:
683 Debug.Print("Task canceled");
685 case TaskStatus.Faulted:
686 Debug.Print("Unhandled exception");
694 DisplayException(ModuleName, ex);
700 /// sets the number of input reports the host HID driver store.
703 /// <param name="hidDeviceObject"> a handle to the device.</param>
704 /// <param name="numberBuffers"> the requested number of input reports. </param>
707 /// True on success. False on failure.
710 internal Boolean SetNumberOfInputBuffers(SafeFileHandle hidDeviceObject, Int32 numberBuffers)
715 // API function: HidD_SetNumInputBuffers
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
723 // An integer to hold the number of buffers.
725 // Returns: true on success, false on failure.
728 NativeMethods.HidD_SetNumInputBuffers(hidDeviceObject, numberBuffers);
734 DisplayException(ModuleName, ex);