using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; using Microsoft.Win32.SafeHandles; namespace WorkIndicator.Delcom { internal sealed partial class HID { private const String ModuleName = "Hid"; internal HidpCaps Capabilities; internal HiddAttributes DeviceAttributes; internal abstract class ReportIn { internal abstract void Read(SafeFileHandle hidHandle, SafeFileHandle readHandle, SafeFileHandle writeHandle, ref Boolean myDeviceDetected, ref Byte[] readBuffer, ref Boolean success); } internal class InFeatureReport : ReportIn { internal override void Read(SafeFileHandle hidHandle, SafeFileHandle readHandle, SafeFileHandle writeHandle, ref Boolean myDeviceDetected, ref Byte[] inFeatureReportBuffer, ref Boolean success) { try { success = HidD_GetFeature(hidHandle, inFeatureReportBuffer, inFeatureReportBuffer.Length); Debug.Print("HidD_GetFeature success = " + success); } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } } } internal class InputReportViaControlTransfer : ReportIn { internal override void Read(SafeFileHandle hidHandle, SafeFileHandle readHandle, SafeFileHandle writeHandle, ref Boolean myDeviceDetected, ref Byte[] inputReportBuffer, ref Boolean success) { try { success = HidD_GetInputReport(hidHandle, inputReportBuffer, inputReportBuffer.Length + 1); Debug.Print("HidD_GetInputReport success = " + success); } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } } } /// /// For reading Input reports. /// internal class InputReportViaInterruptTransfer : ReportIn { internal void CancelTransfer(SafeFileHandle hidHandle, SafeFileHandle readHandle, SafeFileHandle writeHandle, IntPtr eventObject) { try { // *** // API function: CancelIo // Purpose: Cancels a call to ReadFile // Accepts: the device handle. // Returns: True on success, False on failure. // *** FileIODeclarations.CancelIo(readHandle); Debug.WriteLine("************ReadFile error*************"); //String functionName = "CancelIo"; //Debug.WriteLine(MyDebugging.ResultOfAPICall(functionName)); Debug.WriteLine(""); // The failure may have been because the device was removed, // so close any open handles and // set myDeviceDetected=False to cause the application to // look for the device on the next attempt. if ((!(hidHandle.IsInvalid))) { hidHandle.Close(); } if ((!(readHandle.IsInvalid))) { readHandle.Close(); } if ((!(writeHandle.IsInvalid))) { writeHandle.Close(); } } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } } /// /// Creates an event object for the overlapped structure used with ReadFile. /// /// /// the overlapped structure /// the event object internal void PrepareForOverlappedTransfer(ref NativeOverlapped hidOverlapped, ref IntPtr eventObject) { try { // *** // API function: CreateEvent // Purpose: Creates an event object for the overlapped structure used with ReadFile. // Accepts: // A security attributes structure or IntPtr.Zero. // Manual Reset = False (The system automatically resets the state to nonsignaled // after a waiting thread has been released.) // Initial state = False (not signaled) // An event object name (optional) // Returns: a handle to the event object // *** eventObject = FileIODeclarations.CreateEvent(IntPtr.Zero, false, false, ""); // Set the members of the overlapped structure. hidOverlapped.OffsetLow = 0; hidOverlapped.OffsetHigh = 0; hidOverlapped.EventHandle = eventObject; } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } } /// /// reads an Input report from the device using interrupt transfers. /// /// /// the handle for learning about the device and exchanging Feature reports. /// the handle for reading Input reports from the device. /// the handle for writing Output reports to the device. /// tells whether the device is currently attached. /// contains the requested report. /// read success internal override void Read(SafeFileHandle hidHandle, SafeFileHandle readHandle, SafeFileHandle writeHandle, ref Boolean myDeviceDetected, ref Byte[] inputReportBuffer, ref Boolean success) { IntPtr eventObject = IntPtr.Zero; NativeOverlapped hidOverlapped = new NativeOverlapped(); Int32 numberOfBytesRead = 0; try { // Set up the overlapped structure for ReadFile. PrepareForOverlappedTransfer(ref hidOverlapped, ref eventObject); // Allocate memory for the input buffer and overlapped structure. IntPtr nonManagedBuffer = Marshal.AllocHGlobal(inputReportBuffer.Length); IntPtr nonManagedOverlapped = Marshal.AllocHGlobal(Marshal.SizeOf(hidOverlapped)); Marshal.StructureToPtr(hidOverlapped, nonManagedOverlapped, false); // *** // API function: ReadFile // Purpose: Attempts to read an Input report from the device. // Accepts: // A device handle returned by CreateFile // (for overlapped I/O, CreateFile must have been called with FILE_FLAG_OVERLAPPED), // A pointer to a buffer for storing the report. // The Input report length in bytes returned by HidP_GetCaps, // A pointer to a variable that will hold the number of bytes read. // An overlapped structure whose hEvent member is set to an event object. // Returns: the report in ReadBuffer. // The overlapped call returns immediately, even if the data hasn't been received yet. // To read multiple reports with one ReadFile, increase the size of ReadBuffer // and use NumberOfBytesRead to determine how many reports were returned. // Use a larger buffer if the application can't keep up with reading each report // individually. // *** success = FileIODeclarations.ReadFile(readHandle, nonManagedBuffer, inputReportBuffer.Length, ref numberOfBytesRead, nonManagedOverlapped); if (!success) { Debug.WriteLine("waiting for ReadFile"); // API function: WaitForSingleObject // Purpose: waits for at least one report or a timeout. // Used with overlapped ReadFile. // Accepts: // An event object created with CreateEvent // A timeout value in milliseconds. // Returns: A result code. Int32 result = FileIODeclarations.WaitForSingleObject(eventObject, 3000); // Find out if ReadFile completed or timeout. switch (result) { case FileIODeclarations.WaitObject0: // ReadFile has completed success = true; Debug.WriteLine("ReadFile completed successfully."); // Get the number of bytes read. // API function: GetOverlappedResult // Purpose: gets the result of an overlapped operation. // Accepts: // A device handle returned by CreateFile. // A pointer to an overlapped structure. // A pointer to a variable to hold the number of bytes read. // False to return immediately. // Returns: non-zero on success and the number of bytes read. FileIODeclarations.GetOverlappedResult(readHandle, nonManagedOverlapped, ref numberOfBytesRead, false); break; case FileIODeclarations.WaitTimeout: // Cancel the operation on timeout CancelTransfer(hidHandle, readHandle, writeHandle, eventObject); Debug.WriteLine("Readfile timeout"); success = false; myDeviceDetected = false; break; default: // Cancel the operation on other error. CancelTransfer(hidHandle, readHandle, writeHandle, eventObject); Debug.WriteLine("Readfile undefined error"); success = false; myDeviceDetected = false; break; } } if (success) { // A report was received. // Copy the received data to inputReportBuffer for the application to use. Marshal.Copy(nonManagedBuffer, inputReportBuffer, 0, numberOfBytesRead); } } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } } } /// /// For reports the host sends to the device. /// internal abstract class ReportOut { /// /// Each class that handles writing reports defines a Write method for /// writing a type of report. /// /// /// contains the report ID and report data. /// handle to the device. /// /// /// True on success. False on failure. /// internal abstract Boolean Write(Byte[] reportBuffer, SafeFileHandle deviceHandle); } /// /// For Feature reports the host sends to the device. /// internal class OutFeatureReport : ReportOut { /// /// writes a Feature report to the device. /// /// /// contains the report ID and report data. /// handle to the device. /// /// /// True on success. False on failure. /// internal override Boolean Write(Byte[] outFeatureReportBuffer, SafeFileHandle hidHandle) { try { // *** // API function: HidD_SetFeature // Purpose: Attempts to send a Feature report to the device. // Accepts: // A handle to a HID // A pointer to a buffer containing the report ID and report // The size of the buffer. // Returns: true on success, false on failure. // *** Boolean success = HidD_SetFeature(hidHandle, outFeatureReportBuffer, outFeatureReportBuffer.Length); Debug.Print("HidD_SetFeature success = " + success); return success; } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } } } /// /// For writing Output reports via control transfers /// internal class OutputReportViaControlTransfer : ReportOut { /// /// writes an Output report to the device using a control transfer. /// /// /// contains the report ID and report data. /// handle to the device. /// /// /// True on success. False on failure. /// internal override Boolean Write(Byte[] outputReportBuffer, SafeFileHandle hidHandle) { try { // *** // API function: HidD_SetOutputReport // Purpose: // Attempts to send an Output report to the device using a control transfer. // Requires Windows XP or later. // Accepts: // A handle to a HID // A pointer to a buffer containing the report ID and report // The size of the buffer. // Returns: true on success, false on failure. // *** Boolean success = HidD_SetOutputReport(hidHandle, outputReportBuffer, outputReportBuffer.Length + 1); Debug.Print("HidD_SetOutputReport success = " + success); return success; } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } } } /// /// For Output reports the host sends to the device. /// Uses interrupt or control transfers depending on the device and OS. /// internal class OutputReportViaInterruptTransfer : ReportOut { /// /// writes an Output report to the device. /// /// /// contains the report ID and report data. /// handle to the device. /// /// /// True on success. False on failure. /// internal override Boolean Write(Byte[] outputReportBuffer, SafeFileHandle writeHandle) { try { // The host will use an interrupt transfer if the the HID has an interrupt OUT // endpoint (requires USB 1.1 or later) AND the OS is NOT Windows 98 Gold (original version). // Otherwise the the host will use a control transfer. // The application doesn't have to know or care which type of transfer is used. Int32 numberOfBytesWritten = 0; // *** // API function: WriteFile // Purpose: writes an Output report to the device. // Accepts: // A handle returned by CreateFile // An integer to hold the number of bytes written. // Returns: True on success, False on failure. // *** Boolean success = FileIODeclarations.WriteFile(writeHandle, outputReportBuffer, outputReportBuffer.Length, ref numberOfBytesWritten, IntPtr.Zero); Debug.Print("WriteFile success = " + success); if (!((success))) { if ((!(writeHandle.IsInvalid))) { writeHandle.Close(); } } return success; } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } } } /// /// Remove any Input reports waiting in the buffer. /// /// /// a handle to a device. /// /// /// True on success, False on failure. /// internal Boolean FlushQueue(SafeFileHandle hidHandle) { try { // *** // API function: HidD_FlushQueue // Purpose: Removes any Input reports waiting in the buffer. // Accepts: a handle to the device. // Returns: True on success, False on failure. // *** Boolean success = HidD_FlushQueue(hidHandle); return success; } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } } /// /// Retrieves a structure with information about a device's capabilities. /// /// /// a handle to a device. /// /// /// An HIDP_CAPS structure. /// internal HidpCaps GetDeviceCapabilities(SafeFileHandle hidHandle) { IntPtr preparsedData = new IntPtr(); try { // *** // API function: HidD_GetPreparsedData // Purpose: retrieves a pointer to a buffer containing information about the device's capabilities. // HidP_GetCaps and other API functions require a pointer to the buffer. // Requires: // A handle returned by CreateFile. // A pointer to a buffer. // Returns: // True on success, False on failure. // *** HidD_GetPreparsedData(hidHandle, ref preparsedData); // *** // API function: HidP_GetCaps // Purpose: find out a device's capabilities. // For standard devices such as joysticks, you can find out the specific // capabilities of the device. // For a custom device where the software knows what the device is capable of, // this call may be unneeded. // Accepts: // A pointer returned by HidD_GetPreparsedData // A pointer to a HIDP_CAPS structure. // Returns: True on success, False on failure. // *** Int32 result = HidP_GetCaps(preparsedData, ref Capabilities); if ((result != 0)) { Debug.WriteLine(""); Debug.WriteLine(" Usage: " + Convert.ToString(Capabilities.Usage, 16)); Debug.WriteLine(" Usage Page: " + Convert.ToString(Capabilities.UsagePage, 16)); Debug.WriteLine(" Input Report Byte Length: " + Capabilities.InputReportByteLength); Debug.WriteLine(" Output Report Byte Length: " + Capabilities.OutputReportByteLength); Debug.WriteLine(" Feature Report Byte Length: " + Capabilities.FeatureReportByteLength); Debug.WriteLine(" Number of Link Collection Nodes: " + Capabilities.NumberLinkCollectionNodes); Debug.WriteLine(" Number of Input Button Caps: " + Capabilities.NumberInputButtonCaps); Debug.WriteLine(" Number of Input Value Caps: " + Capabilities.NumberInputValueCaps); Debug.WriteLine(" Number of Input Data Indices: " + Capabilities.NumberInputDataIndices); Debug.WriteLine(" Number of Output Button Caps: " + Capabilities.NumberOutputButtonCaps); Debug.WriteLine(" Number of Output Value Caps: " + Capabilities.NumberOutputValueCaps); Debug.WriteLine(" Number of Output Data Indices: " + Capabilities.NumberOutputDataIndices); Debug.WriteLine(" Number of Feature Button Caps: " + Capabilities.NumberFeatureButtonCaps); Debug.WriteLine(" Number of Feature Value Caps: " + Capabilities.NumberFeatureValueCaps); Debug.WriteLine(" Number of Feature Data Indices: " + Capabilities.NumberFeatureDataIndices); // *** // API function: HidP_GetValueCaps // Purpose: retrieves a buffer containing an array of HidP_ValueCaps structures. // Each structure defines the capabilities of one value. // This application doesn't use this data. // Accepts: // A report type enumerator from hidpi.h, // A pointer to a buffer for the returned array, // The NumberInputValueCaps member of the device's HidP_Caps structure, // A pointer to the PreparsedData structure returned by HidD_GetPreparsedData. // Returns: True on success, False on failure. // *** Int32 vcSize = Capabilities.NumberInputValueCaps; Byte[] valueCaps = new Byte[vcSize]; HidP_GetValueCaps(HidPInput, valueCaps, ref vcSize, preparsedData); //result = HidP_GetValueCaps(HidP_Input, ref valueCaps[0], ref Capabilities.NumberInputValueCaps, preparsedData); // (To use this data, copy the ValueCaps byte array into an array of structures.) } } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } finally { // *** // API function: HidD_FreePreparsedData // Purpose: frees the buffer reserved by HidD_GetPreparsedData. // Accepts: A pointer to the PreparsedData structure returned by HidD_GetPreparsedData. // Returns: True on success, False on failure. // *** if (preparsedData != IntPtr.Zero) { HidD_FreePreparsedData(preparsedData); } } return Capabilities; } /// /// Creates a 32-bit Usage from the Usage Page and Usage ID. /// Determines whether the Usage is a system mouse or keyboard. /// Can be modified to detect other Usages. /// /// /// a HIDP_CAPS structure retrieved with HidP_GetCaps. /// /// /// A String describing the Usage. /// internal String GetHidUsage(HidpCaps capabilities) { String usageDescription = ""; try { // Create32-bit Usage from Usage Page and Usage ID. Int32 usage = capabilities.UsagePage * 256 + capabilities.Usage; if (usage == Convert.ToInt32(0X102)) { usageDescription = "mouse"; } if (usage == Convert.ToInt32(0X106)) { usageDescription = "keyboard"; } } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } return usageDescription; } /// /// Retrieves the number of Input reports the host can store. /// /// /// a handle to a device /// an integer to hold the returned value. /// /// /// True on success, False on failure. /// internal Boolean GetNumberOfInputBuffers(SafeFileHandle hidDeviceObject, ref Int32 numberOfInputBuffers) { try { Boolean success; if (!IsWindows98Gold()) { // *** // API function: HidD_GetNumInputBuffers // Purpose: retrieves the number of Input reports the host can store. // Not supported by Windows 98 Gold. // If the buffer is full and another report arrives, the host drops the // ldest report. // Accepts: a handle to a device and an integer to hold the number of buffers. // Returns: True on success, False on failure. // *** success = HidD_GetNumInputBuffers(hidDeviceObject, ref numberOfInputBuffers); } else { // Under Windows 98 Gold, the number of buffers is fixed at 2. numberOfInputBuffers = 2; success = true; } return success; } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } } /// /// sets the number of input reports the host will store. /// Requires Windows XP or later. /// /// /// a handle to the device. /// the requested number of input reports. /// /// /// True on success. False on failure. /// internal Boolean SetNumberOfInputBuffers(SafeFileHandle hidDeviceObject, Int32 numberBuffers) { try { if (!IsWindows98Gold()) { // *** // API function: HidD_SetNumInputBuffers // Purpose: Sets the number of Input reports the host can store. // If the buffer is full and another report arrives, the host drops the // oldest report. // Requires: // A handle to a HID // An integer to hold the number of buffers. // Returns: true on success, false on failure. // *** HidD_SetNumInputBuffers(hidDeviceObject, numberBuffers); return true; } // Not supported under Windows 98 Gold. return false; } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } } /// /// Find out if the current operating system is Windows XP or later. /// (Windows XP or later is required for HidD_GetInputReport and HidD_SetInputReport.) /// internal Boolean IsWindowsXpOrLater() { try { OperatingSystem myEnvironment = Environment.OSVersion; // Windows XP is version 5.1. Version versionXP = new Version(5, 1); if (myEnvironment.Version >= versionXP) { Debug.Write("The OS is Windows XP or later."); return true; } Debug.Write("The OS is earlier than Windows XP."); return false; } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } } /// /// Find out if the current operating system is Windows 98 Gold (original version). /// Windows 98 Gold does not support the following: /// Interrupt OUT transfers (WriteFile uses control transfers and Set_Report). /// HidD_GetNumInputBuffers and HidD_SetNumInputBuffers /// (Not yet tested on a Windows 98 Gold system.) /// internal Boolean IsWindows98Gold() { try { OperatingSystem myEnvironment = Environment.OSVersion; // Windows 98 Gold is version 4.10 with a build number less than 2183. Version version98Se = new Version(4, 10, 2183); Boolean result; if (myEnvironment.Version < version98Se) { Debug.Write("The OS is Windows 98 Gold."); result = true; } else { Debug.Write("The OS is more recent than Windows 98 Gold."); result = false; } return result; } catch (Exception ex) { DisplayException(ModuleName, ex); throw; } } /// /// Provides a central mechanism for exception handling. /// Displays a message box that describes the exception. /// /// /// the module where the exception occurred. /// the exception internal static void DisplayException(String moduleName, Exception e) { // Create an error message. string message = "Exception: " + e.Message + "\r\n" + "Module: " + moduleName + "\r\n" + "Method: " + e.TargetSite.Name; //MessageBox.Show(message, caption, MessageBoxButtons.OK); Debug.Write(message); } } }