KFDtool/sw/control/HidLibrary/HidDevice.cs

648 lines
23 KiB
C#
Raw Normal View History

2019-07-29 15:24:10 -06:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace HidLibrary
{
public class HidDevice : IHidDevice, IDisposable
{
protected delegate HidDeviceData ReadDelegate();
protected delegate HidReport ReadReportDelegate();
private delegate bool WriteDelegate(byte[] data);
private delegate bool WriteReportDelegate(HidReport report);
private readonly string _description;
private readonly string _devicePath;
private readonly HidDeviceAttributes _deviceAttributes;
private readonly HidDeviceCapabilities _deviceCapabilities;
private DeviceMode _deviceReadMode = DeviceMode.NonOverlapped;
private DeviceMode _deviceWriteMode = DeviceMode.NonOverlapped;
private readonly HidDeviceEventMonitor _deviceEventMonitor;
private bool _monitorDeviceEvents;
public IntPtr ReadHandle
{
get;
private set;
}
public IntPtr WriteHandle
{
get;
private set;
}
public bool IsOpen
{
get;
private set;
}
public bool IsConnected => HidDevices.IsConnected(_devicePath);
public string Description => _description;
public HidDeviceCapabilities Capabilities => _deviceCapabilities;
public HidDeviceAttributes Attributes => _deviceAttributes;
public string DevicePath => _devicePath;
public bool MonitorDeviceEvents
{
get
{
return _monitorDeviceEvents;
}
set
{
if (value & !_monitorDeviceEvents)
{
_deviceEventMonitor.Init();
}
_monitorDeviceEvents = value;
}
}
public event InsertedEventHandler Inserted;
public event RemovedEventHandler Removed;
internal HidDevice(string devicePath, string description = null)
{
_deviceEventMonitor = new HidDeviceEventMonitor(this);
_deviceEventMonitor.Inserted += DeviceEventMonitorInserted;
_deviceEventMonitor.Removed += DeviceEventMonitorRemoved;
_devicePath = devicePath;
_description = description;
try
{
IntPtr intPtr = OpenDeviceIO(_devicePath, 0u);
_deviceAttributes = GetDeviceAttributes(intPtr);
_deviceCapabilities = GetDeviceCapabilities(intPtr);
CloseDeviceIO(intPtr);
}
catch (Exception innerException)
{
throw new Exception($"Error querying HID device '{devicePath}'.", innerException);
}
}
public override string ToString()
{
return $"VendorID={_deviceAttributes.VendorHexId}, ProductID={_deviceAttributes.ProductHexId}, Version={_deviceAttributes.Version}, DevicePath={_devicePath}";
}
public void OpenDevice()
{
OpenDevice(DeviceMode.NonOverlapped, DeviceMode.NonOverlapped);
}
public void OpenDevice(DeviceMode readMode, DeviceMode writeMode)
{
if (!IsOpen)
{
_deviceReadMode = readMode;
_deviceWriteMode = writeMode;
try
{
ReadHandle = OpenDeviceIO(_devicePath, readMode, 2147483648u);
WriteHandle = OpenDeviceIO(_devicePath, writeMode, 1073741824u);
}
catch (Exception innerException)
{
IsOpen = false;
throw new Exception("Error opening HID device.", innerException);
}
IntPtr intPtr = ReadHandle;
bool num = intPtr.ToInt32() != -1;
intPtr = WriteHandle;
IsOpen = (num & (intPtr.ToInt32() != -1));
}
}
public void CloseDevice()
{
if (IsOpen)
{
CloseDeviceIO(ReadHandle);
CloseDeviceIO(WriteHandle);
IsOpen = false;
}
}
public HidDeviceData Read()
{
return Read(0);
}
public void Read(ReadCallback callback)
{
ReadDelegate readDelegate = Read;
HidAsyncState @object = new HidAsyncState(readDelegate, callback);
readDelegate.BeginInvoke(EndRead, @object);
}
public HidDeviceData Read(int timeout)
{
if (IsConnected)
{
if (!IsOpen)
{
OpenDevice();
}
try
{
return ReadData(timeout);
}
catch
{
return new HidDeviceData(HidDeviceData.ReadStatus.ReadError);
}
}
return new HidDeviceData(HidDeviceData.ReadStatus.NotConnected);
}
public void ReadReport(ReadReportCallback callback)
{
ReadReportDelegate readReportDelegate = ReadReport;
HidAsyncState @object = new HidAsyncState(readReportDelegate, callback);
readReportDelegate.BeginInvoke(EndReadReport, @object);
}
public HidReport ReadReport(int timeout)
{
return new HidReport(Capabilities.InputReportByteLength, Read(timeout));
}
public HidReport ReadReport()
{
return ReadReport(0);
}
public bool ReadFeatureData(out byte[] data, byte reportId = 0)
{
if (_deviceCapabilities.FeatureReportByteLength > 0)
{
data = new byte[_deviceCapabilities.FeatureReportByteLength];
byte[] array = CreateFeatureOutputBuffer();
array[0] = reportId;
IntPtr intPtr = IntPtr.Zero;
bool flag = false;
try
{
intPtr = OpenDeviceIO(_devicePath, 0u);
flag = NativeMethods.HidD_GetFeature(intPtr, array, array.Length);
if (flag)
{
Array.Copy(array, 0, data, 0, Math.Min(data.Length, _deviceCapabilities.FeatureReportByteLength));
}
}
catch (Exception innerException)
{
throw new Exception($"Error accessing HID device '{_devicePath}'.", innerException);
}
finally
{
if (intPtr != IntPtr.Zero)
{
CloseDeviceIO(intPtr);
}
}
return flag;
}
data = new byte[0];
return false;
}
public bool ReadProduct(out byte[] data)
{
data = new byte[64];
IntPtr intPtr = IntPtr.Zero;
bool result = false;
try
{
intPtr = OpenDeviceIO(_devicePath, 0u);
result = NativeMethods.HidD_GetProductString(intPtr, ref data[0], data.Length);
}
catch (Exception innerException)
{
throw new Exception($"Error accessing HID device '{_devicePath}'.", innerException);
}
finally
{
if (intPtr != IntPtr.Zero)
{
CloseDeviceIO(intPtr);
}
}
return result;
}
public bool ReadManufacturer(out byte[] data)
{
data = new byte[64];
IntPtr intPtr = IntPtr.Zero;
bool result = false;
try
{
intPtr = OpenDeviceIO(_devicePath, 0u);
result = NativeMethods.HidD_GetManufacturerString(intPtr, ref data[0], data.Length);
}
catch (Exception innerException)
{
throw new Exception($"Error accessing HID device '{_devicePath}'.", innerException);
}
finally
{
if (intPtr != IntPtr.Zero)
{
CloseDeviceIO(intPtr);
}
}
return result;
}
public bool ReadSerialNumber(out byte[] data)
{
data = new byte[64];
IntPtr intPtr = IntPtr.Zero;
bool result = false;
try
{
intPtr = OpenDeviceIO(_devicePath, 0u);
result = NativeMethods.HidD_GetSerialNumberString(intPtr, ref data[0], data.Length);
}
catch (Exception innerException)
{
throw new Exception($"Error accessing HID device '{_devicePath}'.", innerException);
}
finally
{
if (intPtr != IntPtr.Zero)
{
CloseDeviceIO(intPtr);
}
}
return result;
}
public void Write(byte[] data, WriteCallback callback)
{
WriteDelegate writeDelegate = Write;
HidAsyncState @object = new HidAsyncState(writeDelegate, callback);
writeDelegate.BeginInvoke(data, EndWrite, @object);
}
public bool Write(byte[] data)
{
return Write(data, 0);
}
public bool Write(byte[] data, int timeout)
{
if (IsConnected)
{
if (!IsOpen)
{
OpenDevice();
}
try
{
return WriteData(data, timeout);
}
catch
{
return false;
}
}
return false;
}
public void WriteReport(HidReport report, WriteCallback callback)
{
WriteReportDelegate writeReportDelegate = WriteReport;
HidAsyncState @object = new HidAsyncState(writeReportDelegate, callback);
writeReportDelegate.BeginInvoke(report, EndWriteReport, @object);
}
public bool WriteReport(HidReport report)
{
return WriteReport(report, 0);
}
public bool WriteReport(HidReport report, int timeout)
{
return Write(report.GetBytes(), timeout);
}
public HidReport CreateReport()
{
return new HidReport(Capabilities.OutputReportByteLength);
}
public bool WriteFeatureData(byte[] data)
{
if (_deviceCapabilities.FeatureReportByteLength > 0)
{
byte[] array = CreateFeatureOutputBuffer();
Array.Copy(data, 0, array, 0, Math.Min(data.Length, _deviceCapabilities.FeatureReportByteLength));
IntPtr intPtr = IntPtr.Zero;
bool result = false;
try
{
intPtr = OpenDeviceIO(_devicePath, 0u);
result = NativeMethods.HidD_SetFeature(intPtr, array, array.Length);
}
catch (Exception innerException)
{
throw new Exception($"Error accessing HID device '{_devicePath}'.", innerException);
}
finally
{
if (intPtr != IntPtr.Zero)
{
CloseDeviceIO(intPtr);
}
}
return result;
}
return false;
}
protected static void EndRead(IAsyncResult ar)
{
HidAsyncState hidAsyncState = (HidAsyncState)ar.AsyncState;
ReadDelegate readDelegate = (ReadDelegate)hidAsyncState.CallerDelegate;
ReadCallback readCallback = (ReadCallback)hidAsyncState.CallbackDelegate;
HidDeviceData data = readDelegate.EndInvoke(ar);
readCallback?.Invoke(data);
}
protected static void EndReadReport(IAsyncResult ar)
{
HidAsyncState hidAsyncState = (HidAsyncState)ar.AsyncState;
ReadReportDelegate readReportDelegate = (ReadReportDelegate)hidAsyncState.CallerDelegate;
ReadReportCallback readReportCallback = (ReadReportCallback)hidAsyncState.CallbackDelegate;
HidReport report = readReportDelegate.EndInvoke(ar);
readReportCallback?.Invoke(report);
}
private static void EndWrite(IAsyncResult ar)
{
HidAsyncState hidAsyncState = (HidAsyncState)ar.AsyncState;
WriteDelegate writeDelegate = (WriteDelegate)hidAsyncState.CallerDelegate;
WriteCallback writeCallback = (WriteCallback)hidAsyncState.CallbackDelegate;
bool success = writeDelegate.EndInvoke(ar);
writeCallback?.Invoke(success);
}
private static void EndWriteReport(IAsyncResult ar)
{
HidAsyncState hidAsyncState = (HidAsyncState)ar.AsyncState;
WriteReportDelegate writeReportDelegate = (WriteReportDelegate)hidAsyncState.CallerDelegate;
WriteCallback writeCallback = (WriteCallback)hidAsyncState.CallbackDelegate;
bool success = writeReportDelegate.EndInvoke(ar);
writeCallback?.Invoke(success);
}
private byte[] CreateInputBuffer()
{
return CreateBuffer(Capabilities.InputReportByteLength - 1);
}
private byte[] CreateOutputBuffer()
{
return CreateBuffer(Capabilities.OutputReportByteLength - 1);
}
private byte[] CreateFeatureOutputBuffer()
{
return CreateBuffer(Capabilities.FeatureReportByteLength - 1);
}
private static byte[] CreateBuffer(int length)
{
byte[] array = null;
Array.Resize(ref array, length + 1);
return array;
}
private static HidDeviceAttributes GetDeviceAttributes(IntPtr hidHandle)
{
NativeMethods.HIDD_ATTRIBUTES attributes = default(NativeMethods.HIDD_ATTRIBUTES);
attributes.Size = Marshal.SizeOf((object)attributes);
NativeMethods.HidD_GetAttributes(hidHandle, ref attributes);
return new HidDeviceAttributes(attributes);
}
private static HidDeviceCapabilities GetDeviceCapabilities(IntPtr hidHandle)
{
NativeMethods.HIDP_CAPS capabilities = default(NativeMethods.HIDP_CAPS);
IntPtr preparsedData = default(IntPtr);
if (NativeMethods.HidD_GetPreparsedData(hidHandle, ref preparsedData))
{
NativeMethods.HidP_GetCaps(preparsedData, ref capabilities);
NativeMethods.HidD_FreePreparsedData(preparsedData);
}
return new HidDeviceCapabilities(capabilities);
}
private bool WriteData(byte[] data, int timeout)
{
if (_deviceCapabilities.OutputReportByteLength > 0)
{
byte[] array = CreateOutputBuffer();
uint lpNumberOfBytesWritten = 0u;
Array.Copy(data, 0, array, 0, Math.Min(data.Length, _deviceCapabilities.OutputReportByteLength));
if (_deviceWriteMode != DeviceMode.Overlapped)
{
try
{
NativeOverlapped lpOverlapped = default(NativeOverlapped);
return NativeMethods.WriteFile(WriteHandle, array, (uint)array.Length, out lpNumberOfBytesWritten, ref lpOverlapped);
}
catch
{
return false;
}
}
NativeMethods.SECURITY_ATTRIBUTES securityAttributes = default(NativeMethods.SECURITY_ATTRIBUTES);
NativeOverlapped lpOverlapped2 = default(NativeOverlapped);
int dwMilliseconds = (timeout <= 0) ? 65535 : timeout;
securityAttributes.lpSecurityDescriptor = IntPtr.Zero;
securityAttributes.bInheritHandle = true;
securityAttributes.nLength = Marshal.SizeOf((object)securityAttributes);
lpOverlapped2.OffsetLow = 0;
lpOverlapped2.OffsetHigh = 0;
lpOverlapped2.EventHandle = NativeMethods.CreateEvent(ref securityAttributes, Convert.ToInt32(false), Convert.ToInt32(true), "");
try
{
NativeMethods.WriteFile(WriteHandle, array, (uint)array.Length, out lpNumberOfBytesWritten, ref lpOverlapped2);
}
catch
{
return false;
}
switch (NativeMethods.WaitForSingleObject(lpOverlapped2.EventHandle, dwMilliseconds))
{
case 0u:
return true;
case 258u:
return false;
case uint.MaxValue:
return false;
default:
return false;
}
}
return false;
}
protected HidDeviceData ReadData(int timeout)
{
byte[] array = new byte[0];
HidDeviceData.ReadStatus status = HidDeviceData.ReadStatus.NoDataRead;
if (_deviceCapabilities.InputReportByteLength > 0)
{
uint lpNumberOfBytesRead = 0u;
array = CreateInputBuffer();
if (_deviceReadMode != DeviceMode.Overlapped)
{
try
{
NativeOverlapped lpOverlapped = default(NativeOverlapped);
NativeMethods.ReadFile(ReadHandle, array, (uint)array.Length, out lpNumberOfBytesRead, ref lpOverlapped);
status = HidDeviceData.ReadStatus.Success;
}
catch
{
status = HidDeviceData.ReadStatus.ReadError;
}
}
else
{
NativeMethods.SECURITY_ATTRIBUTES securityAttributes = default(NativeMethods.SECURITY_ATTRIBUTES);
NativeOverlapped lpOverlapped2 = default(NativeOverlapped);
int dwMilliseconds = (timeout <= 0) ? 65535 : timeout;
securityAttributes.lpSecurityDescriptor = IntPtr.Zero;
securityAttributes.bInheritHandle = true;
securityAttributes.nLength = Marshal.SizeOf((object)securityAttributes);
lpOverlapped2.OffsetLow = 0;
lpOverlapped2.OffsetHigh = 0;
lpOverlapped2.EventHandle = NativeMethods.CreateEvent(ref securityAttributes, Convert.ToInt32(false), Convert.ToInt32(true), string.Empty);
try
{
NativeMethods.ReadFile(ReadHandle, array, (uint)array.Length, out lpNumberOfBytesRead, ref lpOverlapped2);
switch (NativeMethods.WaitForSingleObject(lpOverlapped2.EventHandle, dwMilliseconds))
{
case 0u:
status = HidDeviceData.ReadStatus.Success;
break;
case 258u:
status = HidDeviceData.ReadStatus.WaitTimedOut;
array = new byte[0];
break;
case uint.MaxValue:
status = HidDeviceData.ReadStatus.WaitFail;
array = new byte[0];
break;
default:
status = HidDeviceData.ReadStatus.NoDataRead;
array = new byte[0];
break;
}
}
catch
{
status = HidDeviceData.ReadStatus.ReadError;
}
finally
{
CloseDeviceIO(lpOverlapped2.EventHandle);
}
}
}
return new HidDeviceData(array, status);
}
private static IntPtr OpenDeviceIO(string devicePath, uint deviceAccess)
{
return OpenDeviceIO(devicePath, DeviceMode.NonOverlapped, deviceAccess);
}
private static IntPtr OpenDeviceIO(string devicePath, DeviceMode deviceMode, uint deviceAccess)
{
NativeMethods.SECURITY_ATTRIBUTES lpSecurityAttributes = default(NativeMethods.SECURITY_ATTRIBUTES);
int dwFlagsAndAttributes = 0;
if (deviceMode == DeviceMode.Overlapped)
{
dwFlagsAndAttributes = 1073741824;
}
lpSecurityAttributes.lpSecurityDescriptor = IntPtr.Zero;
lpSecurityAttributes.bInheritHandle = true;
lpSecurityAttributes.nLength = Marshal.SizeOf((object)lpSecurityAttributes);
return NativeMethods.CreateFile(devicePath, deviceAccess, 3, ref lpSecurityAttributes, 3, dwFlagsAndAttributes, 0);
}
private static void CloseDeviceIO(IntPtr handle)
{
if (Environment.OSVersion.Version.Major > 5)
{
NativeMethods.CancelIoEx(handle, IntPtr.Zero);
}
NativeMethods.CloseHandle(handle);
}
private void DeviceEventMonitorInserted()
{
if (IsOpen)
{
OpenDevice();
}
if (this.Inserted != null)
{
this.Inserted();
}
}
private void DeviceEventMonitorRemoved()
{
if (IsOpen)
{
CloseDevice();
}
if (this.Removed != null)
{
this.Removed();
}
}
public void Dispose()
{
if (MonitorDeviceEvents)
{
MonitorDeviceEvents = false;
}
if (IsOpen)
{
CloseDevice();
}
}
}
}