using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; namespace DTS.DASLib.Connection.USBFramework { public class HIDevice: IDisposable { // Used in error messages. const string ModuleName = "Hid"; public HIDDeclarations.HIDP_CAPS Capabilities; public HIDDeclarations.HIDD_ATTRIBUTES DeviceAttributes; public void Dispose() { } // For viewing results of API calls in debug.write statements: //static Debugging MyDebugging = new Debugging(); public abstract class DeviceReport { // For reports that the device sends to the host. internal int Result; // Each DeviceReport class defines a ProtectedRead method for reading a type of report. // ProtectedRead and Read are declared as Subs rather than as Functions because // asynchronous reads use a callback method that can access parameters passed by ByRef // but not Function return values. protected abstract void ProtectedRead(int readHandle, int hidHandle, int writeHandle, ref bool myDeviceDetected, ref byte[] readBuffer, ref bool success); public void Read(int readHandle, int hidHandle, int writeHandle, ref bool myDeviceDetected, ref byte[] readBuffer, ref bool success) { // Purpose : Calls the overridden ProtectedRead routine. // Enables other classes to override ProtectedRead // while limiting access as Friend. // (Directly declaring Write as Friend MustOverride causes the // compiler warning : "Other languages may permit Friend // Overridable members to be overridden.") // Accepts : readHandle - a handle for reading from the device. // hidHandle - a handle for other device communications. // myDeviceDetected - tells whether the device is currently // attached and communicating. // readBuffer - a byte array to hold the report ID and report data. // success - read success { // Request the report. ProtectedRead(readHandle, hidHandle, writeHandle, ref myDeviceDetected, ref readBuffer, ref success); } } } internal class InFeatureReport: DeviceReport { // For reading Feature reports. protected override void ProtectedRead(int readHandle, int hidHandle, int writeHandle, ref bool myDeviceDetected, ref byte[] inFeatureReportBuffer, ref bool success) { // Purpose : reads a Feature report from the device. // Accepts : readHandle - the handle for reading from the device. // hidHandle - the handle for other device communications. // myDeviceDetected - tells whether the device is currently attached. // readBuffer - contains the requested report. // success - read success try { // *** // API function: HidD_GetFeature // Attempts to read a Feature report from the device. // Requires: // 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. // *** success = HIDDeclarations.HidD_GetFeature(hidHandle, ref inFeatureReportBuffer[0], inFeatureReportBuffer.Length); } catch(System.Exception ex) { HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex); } } } public class InputReport: DeviceReport { // For reading Input reports. bool ReadyForOverlappedTransfer; // initialize to false internal void CancelTransfer(int readHandle, int hidHandle) { // Purpose : closes open handles to a device. // Accepts : ReadHandle - the handle for reading from the device. // HIDHandle - the handle for other device communications. try { // *** // API function: CancelIo // Purpose: Cancels a call to ReadFile // Accepts: the device handle. // Returns: True on success, False on failure. // *** Result = FileIODeclarations.CancelIo(readHandle); // 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 != 0) { FileIODeclarations.CloseHandle(hidHandle); } if(hidHandle != 0) { FileIODeclarations.CloseHandle(readHandle); } } catch(System.Exception ex) { HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex); } } internal void PrepareForOverlappedTransfer(ref FileIODeclarations.OVERLAPPED hidOverlapped, ref int eventObject) { // Purpose : Creates an event object for the overlapped structure used with // : ReadFile. // ; Called before the first call to ReadFile. FileIODeclarations.SECURITY_ATTRIBUTES Security = new FileIODeclarations.SECURITY_ATTRIBUTES(); try { // Values for the SECURITY_ATTRIBUTES structure: Security.lpSecurityDescriptor = 0; Security.bInheritHandle = System.Convert.ToInt32(true); Security.nLength = Marshal.SizeOf(Security); // *** // API function: CreateEvent // Purpose: Creates an event object for the overlapped structure used with ReadFile. // Accepts: // A security attributes structure. // Manual Reset = False (The system automatically resets the state to nonsignaled // after a waiting thread has been released.) // Initial state = True (signaled) // An event object name (optional) // Returns: a handle to the event object // *** eventObject = FileIODeclarations.CreateEvent(ref Security, System.Convert.ToInt32(false), System.Convert.ToInt32(true), ""); // Set the members of the overlapped structure. hidOverlapped.Offset = 0; hidOverlapped.OffsetHigh = 0; hidOverlapped.hEvent = eventObject; ReadyForOverlappedTransfer = true; } catch(System.Exception ex) { HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex); } } protected override void ProtectedRead(int readHandle, int hidHandle, int writeHandle, ref bool myDeviceDetected, ref byte[] inputReportBuffer, ref bool success) { // Purpose : reads an Input report from the device using interrupt transfers. // Accepts : readHandle - the handle for reading from the device. // hidHandle - the handle for other device communications. // myDeviceDetected - tells whether the device is currently attached. // readBuffer - contains the requested report. // success - read success // FileIOApiDeclarations.OVERLAPPED HIDOverlapped = new HID.FileIOApiDeclarations.OVERLAPPED(); { int NumberOfBytesRead = 0; int TotalBytesRead = 0; long Result = 1; while(Result != 0 && TotalBytesRead < inputReportBuffer.Length) { uint ret = FileIODeclarations.WaitForSingleObject(readHandle, 200); switch(ret) { case FileIODeclarations.WAIT_OBJECT_0: Result = FileIODeclarations.ReadFile(readHandle, ref inputReportBuffer[TotalBytesRead], inputReportBuffer.Length, ref NumberOfBytesRead, 0); TotalBytesRead += NumberOfBytesRead; break; case FileIODeclarations.WAIT_TIMEOUT: Result = 0; break; case FileIODeclarations.WAIT_ABANDONED: case FileIODeclarations.WAIT_FAILED: default: // throw new SliceRecorder.Recorder.RecorderException("InputReport.ProtectedRead WaitForSingleObject failed"); break; } } if(0 == Result) { } // If it's the first attempt to read, set up the overlapped structure for ReadFile. if(!ReadyForOverlappedTransfer) { // PrepareForOverlappedTransfer(ref HIDOverlapped, ref EventObject); } success = Result == 1; } } } internal class InputReportViaControlTransfer: DeviceReport { protected override void ProtectedRead(int readHandle, int hidHandle, int writeHandle, ref bool myDeviceDetected, ref byte[] inputReportBuffer, ref bool success) { // Purpose : reads an Input report from the device using a control transfer. // Accepts : readHandle - the handle for reading from the device. // hidHandle - the handle for other device communications. // myDeviceDetected - tells whether the device is currently attached. // readBuffer - contains the requested report. // success - read success try { // API function: HidD_GetInputReport // Purpose: Attempts to read an Input report from the device using a control transfer. // Supported under Windows XP and later only. // Requires: // 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. success = HIDDeclarations.HidD_GetInputReport(hidHandle, ref inputReportBuffer[0], inputReportBuffer.Length); } catch(System.Exception ex) { HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex); } } } public abstract class HostReport { // For reports the host sends to the device. // Each report class defines a ProtectedWrite method for writing a type of report. protected abstract bool ProtectedWrite(int deviceHandle, byte[] reportBuffer); public bool Write(byte[] reportBuffer, int deviceHandle) { bool Success = false; // Purpose : Calls the overridden ProtectedWrite routine. // : This method enables other classes to override ProtectedWrite // : while limiting access as Friend. // : (Directly declaring Write as Friend MustOverride causes the // : compiler(warning) "Other languages may permit Friend // : Overridable members to be overridden.") // Accepts : reportBuffer - contains the report ID and report data. // : deviceHandle - handle to the device. ' // Returns : True on success. False on failure. try { Success = ProtectedWrite(deviceHandle, reportBuffer); } catch(System.Exception ex) { HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex); } return Success; } } internal class OutFeatureReport: HostReport { // For Feature reports the host sends to the device. protected override bool ProtectedWrite(int hidHandle, byte[] outFeatureReportBuffer) { // Purpose : writes a Feature report to the device. // Accepts : hidHandle - a handle to the device. // featureReportBuffer - contains the report ID and report to send. // Returns : True on success. False on failure. bool Success = false; 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. Success = HIDDeclarations.HidD_SetFeature(hidHandle, ref outFeatureReportBuffer[0], outFeatureReportBuffer.Length); } catch(System.Exception ex) { HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex); } return Success; } } public class OutputReport: HostReport { // For Output reports the host sends to the device. // Uses interrupt or control transfers depending on the device and OS. protected override bool ProtectedWrite(int hidHandle, byte[] outputReportBuffer) { // Purpose : writes an Output report to the device. // Accepts : HIDHandle - a handle to the device. // OutputReportBuffer - contains the report ID and report to send. // Returns : True on success. False on failure. int NumberOfBytesWritten = 0; int Result; bool Success = false; 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. // Purpose: writes an Output report to the device. // Accepts: // A handle returned by CreateFile // The output report byte length returned by HidP_GetCaps. // An integer to hold the number of bytes written. // Returns: True on success, False on failure. Result = FileIODeclarations.WriteFile(hidHandle, ref outputReportBuffer[0], outputReportBuffer.Length, ref NumberOfBytesWritten, 0); // Return True on success, False on failure. Success = (Result == 0) ? false : true; } catch(System.Exception ex) { HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex); } return Success; } } internal class OutputReportViaControlTransfer: HostReport { protected override bool ProtectedWrite(int hidHandle, byte[] outputReportBuffer) { // Purpose : writes an Output report to the device using a control transfer. // Accepts : hidHandle - a handle to the device. // outputReportBuffer - contains the report ID and report to send. // Returns : True on success. False on failure. bool Success = false; 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. Success = HIDDeclarations.HidD_SetOutputReport(hidHandle, ref outputReportBuffer[0], outputReportBuffer.Length); } catch(System.Exception ex) { HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex); } return Success; } } public bool FlushQueue(int hidHandle) { // Purpose : Remove any Input reports waiting in the buffer. // Accepts : hidHandle - a handle to a device. // Returns : True on success, False on failure. bool Result = false; 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. // *** Result = HIDDeclarations.HidD_FlushQueue(hidHandle); //Debug.WriteLine(MyDebugging.ResultOfAPICall("HidD_FlushQueue, ReadHandle")); //Debug.WriteLine("Result = " + Result.ToString()); } catch(System.Exception ex) { HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex); } return Result; } public HIDDeclarations.HIDP_CAPS GetDeviceCapabilities(int hidHandle) { // Purpose : Retrieves a structure with information about a device's capabilities. // Accepts : HIDHandle - a handle to a device. // Returns : An HIDP_CAPS structure. byte[] PreparsedDataBytes = new byte[30]; string PreparsedDataString; IntPtr PreparsedDataPointer = new IntPtr(); int Result; bool Success = false; byte[] ValueCaps = new byte[1024]; // (the array size is a guess) 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. // *** Success = HIDDeclarations.HidD_GetPreparsedData(hidHandle, ref PreparsedDataPointer); //Debug.WriteLine(MyDebugging.ResultOfAPICall("HidD_GetPreparsedData")); //Debug.WriteLine(""); // Copy the data at PreparsedDataPointer into a byte array. PreparsedDataString = System.Convert.ToBase64String(PreparsedDataBytes); // *** // 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. // *** Result = HIDDeclarations.HidP_GetCaps(PreparsedDataPointer, ref Capabilities); if(Result != 0) { // *** // 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. // *** Result = HIDDeclarations.HidP_GetValueCaps(HIDDeclarations.HidP_Input, ref ValueCaps[0], ref Capabilities.NumberInputValueCaps, PreparsedDataPointer); // (To use this data, copy the ValueCaps byte array into an array of structures.) // *** // 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. // *** Success = HIDDeclarations.HidD_FreePreparsedData(ref PreparsedDataPointer); } } catch(System.Exception ex) { HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex); } return Capabilities; } public string GetHIDUsage(HIDDeclarations.HIDP_CAPS MyCapabilities) { //'Purpose : 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. //Accepts : MyCapabilities - a HIDP_CAPS structure retrieved with HidP_GetCaps. //'Returns : A string describing the Usage. string UsageDescription= ""; try { //Create32-bit Usage from Usage Page and Usage ID. int Usage = MyCapabilities.UsagePage * 256 + MyCapabilities.Usage; if(Usage == 0x102) UsageDescription = "mouse"; if(Usage == 0x106) UsageDescription = "keyboard"; } catch(System.Exception ex) { HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex); } return UsageDescription; } public bool GetNumberOfInputBuffers(int hidDeviceObject, ref int numberOfInputBuffers) { // Purpose : Retrieves the number of Input reports the host can store. // Accepts : hidDeviceObject - a handle to a device // : numberBuffers - an integer to hold the returned value. // Returns : True on success, False on failure. bool Success = false; try { // *** // 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 // oldest report. // Accepts: a handle to a device and an integer to hold the number of buffers. // Returns: True on success, False on failure. // *** Success = HIDDeclarations.HidD_GetNumInputBuffers(hidDeviceObject, ref numberOfInputBuffers); } catch(System.Exception ex) { HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex); } return Success; } internal bool SetNumberOfInputBuffers(int hidDeviceObject, int numberBuffers) { // Purpose : sets the number of input reports the host will store. // : Requires Windows XP or later. // Accepts : hidDeviceObject - a handle to the device. // : numberBuffers - the requested number of input reports. // Returns : True on success. False on failure. bool Success = false; try { // *** // 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. // *** Success = HIDDeclarations.HidD_SetNumInputBuffers(hidDeviceObject, numberBuffers); } catch(System.Exception ex) { HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex); } return Success; } static public void HandleException(string moduleName, System.Exception e) { string Message = "Exception: " + e.Message + Environment.NewLine + "Module: " + moduleName + Environment.NewLine + "Method: " + e.TargetSite.Name; } } }