using Microsoft.VisualBasic;
using Microsoft.Win32.SafeHandles;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
///
/// For communicating with HID-class USB devices.
/// Includes routines for sending and receiving reports via control transfers and to
/// retrieve information about and configure a HID.
///
///
namespace SSComm
{
internal sealed partial class Hid
{
// Used in error messages.
private const String MODULE_NAME = "Hid";
internal HIDP_CAPS Capabilities;
internal HIDD_ATTRIBUTES DeviceAttributes;
// For viewing results of API calls in debug.write statements:
//internal static Debugging MyDebugging = new Debugging();
///
/// Remove any Input reports waiting in the buffer.
///
///
/// a handle to a device.
///
///
/// True on success, False on failure.
///
internal Boolean FlushQueue(SafeFileHandle hidHandle)
{
Boolean success = 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.
// ***
success = HidD_FlushQueue(hidHandle);
return success;
}
catch (Exception ex)
{
DisplayException(MODULE_NAME, ex);
throw;
}
}
///
/// Retrieves a structure with information about a device's capabilities.
///
///
/// a handle to a device.
///
///
/// An HIDP_CAPS structure.
///
internal HIDP_CAPS GetDeviceCapabilities(SafeFileHandle hidHandle)
{
IntPtr preparsedData = new System.IntPtr();
Int32 result = 0;
Boolean success = false;
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 = 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.
// ***
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];
result = HidP_GetValueCaps(HidP_Input, valueCaps, ref vcSize, preparsedData);
// (To use this data, copy the ValueCaps byte array into an array of structures.)
}
}
catch (Exception ex)
{
DisplayException(MODULE_NAME, 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)
{
success = HidD_FreePreparsedData(preparsedData);
}
}
return Capabilities;
}
///
/// reads a Feature report from the device.
///
///
/// the handle for learning about the device and exchanging Feature reports.
/// tells whether the device is currently attached.
/// contains the requested report.
/// read success
internal Boolean GetFeatureReport(SafeFileHandle hidHandle, ref Byte[] inFeatureReportBuffer)
{
Boolean 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 = HidD_GetFeature(hidHandle, inFeatureReportBuffer, inFeatureReportBuffer.Length);
Debug.Print("HidD_GetFeature success = " + success);
return success;
}
catch (Exception ex)
{
DisplayException(MODULE_NAME, ex);
throw;
}
}
///
/// 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(HIDP_CAPS MyCapabilities)
{
Int32 usage = 0;
String usageDescription = "";
try
{
// Create32-bit Usage from Usage Page and Usage ID.
usage = MyCapabilities.UsagePage * 256 + MyCapabilities.Usage;
if (usage == Convert.ToInt32(0X102))
{
usageDescription = "mouse";
}
if (usage == Convert.ToInt32(0X106))
{
usageDescription = "keyboard";
}
}
catch (Exception ex)
{
DisplayException(MODULE_NAME, ex);
throw;
}
return usageDescription;
}
///
/// reads an Input report from the device using a control transfer.
///
///
/// the handle for learning about the device and exchanging Feature reports.
/// tells whether the device is currently attached.
/// contains the requested report.
/// read success
internal Boolean GetInputReportViaControlTransfer(SafeFileHandle hidHandle, ref Byte[] inputReportBuffer)
{
Boolean 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 = HidD_GetInputReport(hidHandle, inputReportBuffer, inputReportBuffer.Length + 1);
Debug.Print("HidD_GetInputReport success = " + success);
return success;
}
catch (Exception ex)
{
DisplayException(MODULE_NAME, ex);
throw;
}
}
///
/// 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)
{
Boolean success = false;
try
{
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(MODULE_NAME, ex);
throw;
}
}
///
/// 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 Boolean SendFeatureReport(SafeFileHandle hidHandle, Byte[] outFeatureReportBuffer)
{
Boolean 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 = HidD_SetFeature(hidHandle, outFeatureReportBuffer, outFeatureReportBuffer.Length);
Debug.Print("HidD_SetFeature success = " + success);
return success;
}
catch (Exception ex)
{
DisplayException(MODULE_NAME, ex);
throw;
}
}
///
/// 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 Boolean SendOutputReportViaControlTransfer(SafeFileHandle hidHandle, Byte[] outputReportBuffer)
{
Boolean 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 = HidD_SetOutputReport(hidHandle, outputReportBuffer, outputReportBuffer.Length + 1);
Debug.Print("HidD_SetOutputReport success = " + success);
return success;
}
catch (Exception ex)
{
DisplayException(MODULE_NAME, 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;
}
else
{
// Not supported under Windows 98 Gold.
return false;
}
}
catch (Exception ex)
{
DisplayException(MODULE_NAME, 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.
System.Version versionXP = new System.Version(5, 1);
if (myEnvironment.Version >= versionXP)
{
Debug.Write("The OS is Windows XP or later.");
return true;
}
else
{
Debug.Write("The OS is earlier than Windows XP.");
return false;
}
}
catch (Exception ex)
{
DisplayException(MODULE_NAME, 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()
{
Boolean result = false;
try
{
OperatingSystem myEnvironment = Environment.OSVersion;
// Windows 98 Gold is version 4.10 with a build number less than 2183.
System.Version version98SE = new System.Version(4, 10, 2183);
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(MODULE_NAME, 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)
{
String message = null;
String caption = null;
// Create an error message.
//message = "Exception: " + e.Message + ControlChars.CrLf + "Module: " + moduleName + ControlChars.CrLf + "Method: " + e.TargetSite.Name;
caption = "Unexpected Exception";
MessageBox.Show(message, caption, MessageBoxButtons.OK);
Debug.Write(message);
}
}
}