public static bool ReadMultipleReportsFromDevice(ref byte[] inputReportBuffer, int numberOfReports, ref DeviceInformationStructure deviceInformation) { var success = false; var numberOfBytesRead = 0; long pointerToBuffer = 0; // Define a temporary buffer for assembling partial data reads into the completed inputReportBuffer var temporaryBuffer = new Byte[inputReportBuffer.Length]; // Range check the number of reports if (numberOfReports == 0) { Debug.WriteLine( "usbGenericHidCommunication:readMultipleReportsFromDevice(): -> ERROR: You cannot request 0 reports!"); return false; } if (numberOfReports > 128) { Debug.WriteLine( "usbGenericHidCommunication:readMultipleReportsFromDevice(): -> ERROR: Reference application testing does not verify the code for more than 128 reports"); return false; } // The size of our inputReportBuffer must be at least the same size as the input report multiplied by the number of reports requested. if (inputReportBuffer.Length != (deviceInformation.Capabilities.InputReportByteLength * numberOfReports)) { // inputReportBuffer is not the right length! Debug.WriteLine( "usbGenericHidCommunication:readMultipleReportsFromDevice(): -> ERROR: The referenced inputReportBuffer size is incorrect for the number of input reports requested!"); return false; } // The readRawReportFromDevice method will fill the passed read buffer or return false while (pointerToBuffer != (deviceInformation.Capabilities.InputReportByteLength * numberOfReports)) { Debug.WriteLine( "usbGenericHidCommunication:readMultipleReportsFromDevice(): -> Reading from device..."); success = ReadRawReportFromDevice(ref temporaryBuffer, ref numberOfBytesRead, ref deviceInformation); // Was the read successful? if (!success) { return false; } // Copy the received data into the referenced input buffer Array.Copy(temporaryBuffer, 0, inputReportBuffer, pointerToBuffer, numberOfBytesRead); pointerToBuffer += numberOfBytesRead; } return success; }
public static void QueryDeviceCapabilities(ref DeviceInformationStructure deviceInformation) { var preparsedData = new IntPtr(); try { // Get the preparsed data from the HID driver Hid.HidD_GetPreparsedData(deviceInformation.HidHandle, ref preparsedData); // Get the HID device's capabilities var result = Hid.HidP_GetCaps(preparsedData, ref deviceInformation.Capabilities); if ((result == 0)) return; Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Device query results:"); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Usage: {0}", Convert.ToString(deviceInformation.Capabilities.Usage, 16)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Usage Page: {0}", Convert.ToString(deviceInformation.Capabilities.UsagePage, 16)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Input Report Byte Length: {0}", deviceInformation.Capabilities.InputReportByteLength.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Output Report Byte Length: {0}", deviceInformation.Capabilities.OutputReportByteLength.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Feature Report Byte Length: {0}", deviceInformation.Capabilities.FeatureReportByteLength.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Number of Link Collection Nodes: {0}", deviceInformation.Capabilities.NumberLinkCollectionNodes.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Number of Input Button Caps: {0}", deviceInformation.Capabilities.NumberInputButtonCaps.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Number of Input Value Caps: {0}", deviceInformation.Capabilities.NumberInputValueCaps.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Number of Input Data Indices: {0}", deviceInformation.Capabilities.NumberInputDataIndices.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Number of Output Button Caps: {0}", deviceInformation.Capabilities.NumberOutputButtonCaps.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Number of Output Value Caps: {0}", deviceInformation.Capabilities.NumberOutputValueCaps.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Number of Output Data Indices: {0}", deviceInformation.Capabilities.NumberOutputDataIndices.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Number of Feature Button Caps: {0}", deviceInformation.Capabilities.NumberFeatureButtonCaps.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Number of Feature Value Caps: {0}", deviceInformation.Capabilities.NumberFeatureValueCaps.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> Number of Feature Data Indices: {0}", deviceInformation.Capabilities.NumberFeatureDataIndices.ToString(CultureInfo.InvariantCulture)); } catch (Exception) { // Something went badly wrong... this shouldn't happen, so we throw an exception Debug.WriteLine("usbGenericHidCommunication:queryDeviceCapabilities() -> EXECEPTION: An unrecoverable error has occurred!"); throw; } finally { // Free up the memory before finishing if (preparsedData != IntPtr.Zero) { Hid.HidD_FreePreparsedData(preparsedData); } } }
public static bool FindTargetDevice(ref DeviceInformationStructure deviceInformation) { Debug.WriteLine("usbGenericHidCommunication:findTargetDevice() -> Method called"); var listOfDevicePathNames = new String[128]; // 128 is the maximum number of USB devices allowed on a single host var numberOfDevicesFound = 0; try { // Reset the device detection flag var isDeviceDetected = false; deviceInformation.IsDeviceAttached = false; // Get all the devices with the correct HID GUID var deviceFoundByGuid = FindHidDevices(ref listOfDevicePathNames, ref numberOfDevicesFound); if (deviceFoundByGuid) { Debug.WriteLine("usbGenericHidCommunication:findTargetDevice() -> Devices with matching GUID found..."); var listIndex = 0; do { Debug.WriteLine("usbGenericHidCommunication:findTargetDevice() -> Performing CreateFile to listIndex {0}", listIndex.ToString(CultureInfo.InvariantCulture)); deviceInformation.HidHandle = Kernel32.CreateFile(listOfDevicePathNames[listIndex], 0, Constants.FileShareRead | Constants.FileShareWrite, IntPtr.Zero, Constants.OpenExisting, 0, 0); if (!deviceInformation.HidHandle.IsInvalid) { deviceInformation.Attributes.Size = Marshal.SizeOf(deviceInformation.Attributes); var success = Hid.HidD_GetAttributes(deviceInformation.HidHandle, ref deviceInformation.Attributes); if (success) { Debug.WriteLine(string.Format("usbGenericHidCommunication:findTargetDevice() -> Found device with VID {0}, PID {1} and Version number {2}", Convert.ToString(deviceInformation.Attributes.VendorID, 16), Convert.ToString(deviceInformation.Attributes.ProductID, 16), Convert.ToString(deviceInformation.Attributes.VersionNumber, 16))); // Do the VID and PID of the device match our target device? if ((deviceInformation.Attributes.VendorID == deviceInformation.TargetVendorId) && (deviceInformation.Attributes.ProductID == deviceInformation.TargetProductId)) { // Matching device found Debug.WriteLine("usbGenericHidCommunication:findTargetDevice() -> Device with matching VID and PID found!"); isDeviceDetected = true; // Store the device's pathname in the device information deviceInformation.DevicePathName = listOfDevicePathNames[listIndex]; } else { // Wrong device, close the handle Debug.WriteLine("usbGenericHidCommunication:findTargetDevice() -> Device didn't match... Continuing..."); deviceInformation.HidHandle.Close(); } } else { // Something went rapidly south... give up! Debug.WriteLine("usbGenericHidCommunication:findTargetDevice() -> Something bad happened - couldn't fill the HIDD_ATTRIBUTES, giving up!"); deviceInformation.HidHandle.Close(); } } // Move to the next device, or quit if there are no more devices to examine listIndex++; } while (!((isDeviceDetected || (listIndex == numberOfDevicesFound + 1)))); } // If we found a matching device then we need discover more details about the attached device // and then open read and write handles to the device to allow communication if (isDeviceDetected) { // Query the HID device's capabilities (primarily we are only really interested in the // input and output report byte lengths as this allows us to validate information sent // to and from the device does not exceed the devices capabilities. // // We could determine the 'type' of HID device here too, but since this class is only // for generic HID communication we don't care... QueryDeviceCapabilities(ref deviceInformation); // Open the readHandle to the device Debug.WriteLine("usbGenericHidCommunication:findTargetDevice() -> Opening a readHandle to the device"); deviceInformation.ReadHandle = Kernel32.CreateFile( deviceInformation.DevicePathName, Constants.GenericRead, Constants.FileShareRead | Constants.FileShareWrite, IntPtr.Zero, Constants.OpenExisting, Constants.FileFlagOverlapped, 0); // Did we open the readHandle successfully? if (deviceInformation.ReadHandle.IsInvalid) { Debug.WriteLine("usbGenericHidCommunication:findTargetDevice() -> Unable to open a readHandle to the device!"); return false; } Debug.WriteLine("usbGenericHidCommunication:findTargetDevice() -> Opening a writeHandle to the device"); deviceInformation.WriteHandle = Kernel32.CreateFile( deviceInformation.DevicePathName, Constants.GenericWrite, Constants.FileShareRead | Constants.FileShareWrite, IntPtr.Zero, Constants.OpenExisting, 0, 0); // Did we open the writeHandle successfully? if (deviceInformation.WriteHandle.IsInvalid) { Debug.WriteLine("usbGenericHidCommunication:findTargetDevice() -> Unable to open a writeHandle to the device!"); // Attempt to close the writeHandle deviceInformation.WriteHandle.Close(); return false; } // Device is now discovered and ready for use, update the status deviceInformation.IsDeviceAttached = true; return true; } // The device wasn't detected. Debug.WriteLine("usbGenericHidCommunication:findTargetDevice() -> No matching device found!"); return false; } catch (Exception) { Debug.WriteLine("usbGenericHidCommunication:findTargetDevice() -> EXCEPTION: Unknown - device not found"); return false; } }
public static bool ReadRawReportFromDevice(ref byte[] inputReportBuffer, ref int numberOfBytesRead, ref DeviceInformationStructure deviceInformation) { // Make sure a device is attached if (!deviceInformation.IsDeviceAttached) { Debug.WriteLine("usbGenericHidCommunication:readReportFromDevice(): -> No device attached!"); return false; } IntPtr eventObject; var hidOverlapped = new NativeOverlapped(); IntPtr nonManagedBuffer; IntPtr nonManagedOverlapped; try { // Prepare an event object for the overlapped ReadFile eventObject = Kernel32.CreateEvent(IntPtr.Zero, false, false, ""); hidOverlapped.OffsetLow = 0; hidOverlapped.OffsetHigh = 0; hidOverlapped.EventHandle = eventObject; // Allocate memory for the unmanaged input buffer and overlap structure. nonManagedBuffer = Marshal.AllocHGlobal(inputReportBuffer.Length); nonManagedOverlapped = Marshal.AllocHGlobal(Marshal.SizeOf(hidOverlapped)); Marshal.StructureToPtr(hidOverlapped, nonManagedOverlapped, false); // Read the input report buffer Debug.WriteLine("usbGenericHidCommunication:readReportFromDevice(): -> Attempting to ReadFile"); var success = Kernel32.ReadFile( deviceInformation.ReadHandle, nonManagedBuffer, inputReportBuffer.Length, ref numberOfBytesRead, nonManagedOverlapped); if (!success) { // We are now waiting for the FileRead to complete Debug.WriteLine( "usbGenericHidCommunication:readReportFromDevice(): -> ReadFile started, waiting for completion..."); // Wait a maximum of 3 seconds for the FileRead to complete var result = Kernel32.WaitForSingleObject(eventObject, 3000); switch (result) { // Has the ReadFile completed successfully? case Constants.WaitObject0: // Get the number of bytes transferred Kernel32.GetOverlappedResult(deviceInformation.ReadHandle, nonManagedOverlapped, ref numberOfBytesRead, false); Debug.WriteLine("usbGenericHidCommunication:readReportFromDevice(): -> ReadFile successful (overlapped) {0} bytes read", numberOfBytesRead); break; // Did the FileRead operation timeout? case Constants.WaitTimeout: // Cancel the ReadFile operation Kernel32.CancelIo(deviceInformation.ReadHandle); if (!deviceInformation.HidHandle.IsInvalid) deviceInformation.HidHandle.Close(); if (!deviceInformation.ReadHandle.IsInvalid) deviceInformation.ReadHandle.Close(); if (!deviceInformation.WriteHandle.IsInvalid) deviceInformation.WriteHandle.Close(); // Detach the USB device to try to get us back in a known state //detachUsbDevice(); Debug.WriteLine( "usbGenericHidCommunication:readReportFromDevice(): -> ReadFile timedout! USB device detached"); return false; // Some other unspecified error has occurred? default: // Cancel the ReadFile operation // Detach the USB device to try to get us back in a known state Debug.WriteLine( "usbGenericHidCommunication:readReportFromDevice(): -> ReadFile unspecified error! USB device detached"); return false; } } // Report receieved correctly, copy the unmanaged input buffer over to the managed buffer Marshal.Copy(nonManagedBuffer, inputReportBuffer, 0, numberOfBytesRead); Debug.WriteLine( "usbGenericHidCommunication:readReportFromDevice(): -> ReadFile successful {0} bytes read", numberOfBytesRead); } catch (Exception) { // An error - send out some debug and return failure Debug.WriteLine( "usbGenericHidCommunication:readReportFromDevice(): -> EXCEPTION: When attempting to receive an input report"); return false; } // Release non-managed objects before returning Marshal.FreeHGlobal(nonManagedBuffer); Marshal.FreeHGlobal(nonManagedOverlapped); // Close the file handle to release the object Kernel32.CloseHandle(eventObject); return true; }
public static bool WriteRawReportToDevice(byte[] outputReportBuffer, ref DeviceInformationStructure deviceInformation) { // Make sure a device is attached if (!deviceInformation.IsDeviceAttached) { Debug.WriteLine("usbGenericHidCommunication:writeReportToDevice(): -> No device attached!"); return false; } var numberOfBytesWritten = 0; try { // Set an output report via interrupt to the device var success = Kernel32.WriteFile( deviceInformation.WriteHandle, outputReportBuffer, outputReportBuffer.Length, ref numberOfBytesWritten, IntPtr.Zero); Debug.WriteLine(success ? "usbGenericHidCommunication:writeReportToDevice(): -> Write report succeeded" : "usbGenericHidCommunication:writeReportToDevice(): -> Write report failed!"); return success; } catch (Exception) { // An error - send out some debug and return failure Debug.WriteLine( "usbGenericHidCommunication:writeReportToDevice(): -> EXCEPTION: When attempting to send an output report"); return false; } }
public static bool ReadSingleReportFromDevice(ref byte[] inputReportBuffer, ref DeviceInformationStructure deviceInformation) { var numberOfBytesRead = 0; // The size of our inputReportBuffer must be at least the same size as the input report. if (inputReportBuffer.Length != deviceInformation.Capabilities.InputReportByteLength) { // inputReportBuffer is not the right length! Debug.WriteLine( "usbGenericHidCommunication:readSingleReportFromDevice(): -> ERROR: The referenced inputReportBuffer size is incorrect for the input report size!"); return false; } // The readRawReportFromDevice method will fill the passed readBuffer or return false return ReadRawReportFromDevice(ref inputReportBuffer, ref numberOfBytesRead, ref deviceInformation); }
public static bool ReadRawReportFromDeviceAsync(ref byte[] inputReportBuffer, ref int numberOfBytesRead, ref DeviceInformationStructure deviceInformation) { bool success; // Make sure a device is attached if (!deviceInformation.IsDeviceAttached) { Debug.WriteLine("usbGenericHidCommunication:readReportFromDevice(): -> No device attached!"); return false; } IntPtr eventObject; var hidOverlapped = new NativeOverlapped(); IntPtr nonManagedBuffer; IntPtr nonManagedOverlapped; try { // Prepare an event object for the overlapped ReadFile eventObject = Kernel32.CreateEvent(IntPtr.Zero, false, false, ""); hidOverlapped.OffsetLow = 0; hidOverlapped.OffsetHigh = 0; hidOverlapped.EventHandle = eventObject; // Allocate memory for the unmanaged input buffer and overlap structure. nonManagedBuffer = Marshal.AllocHGlobal(inputReportBuffer.Length); nonManagedOverlapped = Marshal.AllocHGlobal(Marshal.SizeOf(hidOverlapped)); Marshal.StructureToPtr(hidOverlapped, nonManagedOverlapped, false); // Read the input report buffer success = Kernel32.ReadFile( deviceInformation.ReadHandle, nonManagedBuffer, inputReportBuffer.Length, ref numberOfBytesRead, nonManagedOverlapped); if(success) Debug.WriteLine("usbGenericHidCommunication:readReportFromDevice(): -> Read Ok"); // Report receieved correctly, copy the unmanaged input buffer over to the managed buffer Marshal.Copy(nonManagedBuffer, inputReportBuffer, 0, numberOfBytesRead); } catch (Exception) { // An error - send out some debug and return failure Debug.WriteLine( "usbGenericHidCommunication:readReportFromDevice(): -> EXCEPTION: When attempting to receive an input report"); return false; } // Release non-managed objects before returning Marshal.FreeHGlobal(nonManagedBuffer); Marshal.FreeHGlobal(nonManagedOverlapped); // Close the file handle to release the object Kernel32.CloseHandle(eventObject); return success; }