| @ -0,0 +1,19 @@ | |||||
| using System.Text; | |||||
| namespace HidLibrary | |||||
| { | |||||
| public static class Extensions | |||||
| { | |||||
| public static string ToUTF8String(this byte[] buffer) | |||||
| { | |||||
| var value = Encoding.UTF8.GetString(buffer); | |||||
| return value.Remove(value.IndexOf((char)0)); | |||||
| } | |||||
| public static string ToUTF16String(this byte[] buffer) | |||||
| { | |||||
| var value = Encoding.Unicode.GetString(buffer); | |||||
| return value.Remove(value.IndexOf((char)0)); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,17 @@ | |||||
| namespace HidLibrary | |||||
| { | |||||
| public class HidAsyncState | |||||
| { | |||||
| private readonly object _callerDelegate; | |||||
| private readonly object _callbackDelegate; | |||||
| public HidAsyncState(object callerDelegate, object callbackDelegate) | |||||
| { | |||||
| _callerDelegate = callerDelegate; | |||||
| _callbackDelegate = callbackDelegate; | |||||
| } | |||||
| public object CallerDelegate { get { return _callerDelegate; } } | |||||
| public object CallbackDelegate { get { return _callbackDelegate; } } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,711 @@ | |||||
| using System; | |||||
| using System.Runtime.InteropServices; | |||||
| using System.Threading; | |||||
| using System.Threading.Tasks; | |||||
| namespace HidLibrary | |||||
| { | |||||
| public class HidDevice : IHidDevice | |||||
| { | |||||
| public event InsertedEventHandler Inserted; | |||||
| public event RemovedEventHandler Removed; | |||||
| 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 ShareMode _deviceShareMode = ShareMode.ShareRead | ShareMode.ShareWrite; | |||||
| private readonly HidDeviceEventMonitor _deviceEventMonitor; | |||||
| private bool _monitorDeviceEvents; | |||||
| protected delegate HidDeviceData ReadDelegate(int timeout); | |||||
| protected delegate HidReport ReadReportDelegate(int timeout); | |||||
| private delegate bool WriteDelegate(byte[] data, int timeout); | |||||
| private delegate bool WriteReportDelegate(HidReport report, int timeout); | |||||
| internal HidDevice(string devicePath, string description = null) | |||||
| { | |||||
| _deviceEventMonitor = new HidDeviceEventMonitor(this); | |||||
| _deviceEventMonitor.Inserted += DeviceEventMonitorInserted; | |||||
| _deviceEventMonitor.Removed += DeviceEventMonitorRemoved; | |||||
| _devicePath = devicePath; | |||||
| _description = description; | |||||
| try | |||||
| { | |||||
| var hidHandle = OpenDeviceIO(_devicePath, NativeMethods.ACCESS_NONE); | |||||
| _deviceAttributes = GetDeviceAttributes(hidHandle); | |||||
| _deviceCapabilities = GetDeviceCapabilities(hidHandle); | |||||
| CloseDeviceIO(hidHandle); | |||||
| } | |||||
| catch (Exception exception) | |||||
| { | |||||
| throw new Exception(string.Format("Error querying HID device '{0}'.", devicePath), exception); | |||||
| } | |||||
| } | |||||
| public IntPtr Handle { get; private set; } | |||||
| public bool IsOpen { get; private set; } | |||||
| public bool IsConnected { get { return HidDevices.IsConnected(_devicePath); } } | |||||
| public string Description { get { return _description; } } | |||||
| public HidDeviceCapabilities Capabilities { get { return _deviceCapabilities; } } | |||||
| public HidDeviceAttributes Attributes { get { return _deviceAttributes; } } | |||||
| public string DevicePath { get { return _devicePath; } } | |||||
| public bool MonitorDeviceEvents | |||||
| { | |||||
| get { return _monitorDeviceEvents; } | |||||
| set | |||||
| { | |||||
| if (value & _monitorDeviceEvents == false) _deviceEventMonitor.Init(); | |||||
| _monitorDeviceEvents = value; | |||||
| } | |||||
| } | |||||
| public override string ToString() | |||||
| { | |||||
| return string.Format("VendorID={0}, ProductID={1}, Version={2}, DevicePath={3}", | |||||
| _deviceAttributes.VendorHexId, | |||||
| _deviceAttributes.ProductHexId, | |||||
| _deviceAttributes.Version, | |||||
| _devicePath); | |||||
| } | |||||
| public void OpenDevice() | |||||
| { | |||||
| OpenDevice(DeviceMode.NonOverlapped, DeviceMode.NonOverlapped, ShareMode.ShareRead | ShareMode.ShareWrite); | |||||
| } | |||||
| public void OpenDevice(DeviceMode readMode, DeviceMode writeMode, ShareMode shareMode) | |||||
| { | |||||
| if (IsOpen) return; | |||||
| _deviceReadMode = readMode; | |||||
| _deviceWriteMode = writeMode; | |||||
| _deviceShareMode = shareMode; | |||||
| try | |||||
| { | |||||
| Handle = OpenDeviceIO(_devicePath, readMode, NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE, shareMode); | |||||
| } | |||||
| catch (Exception exception) | |||||
| { | |||||
| IsOpen = false; | |||||
| throw new Exception("Error opening HID device.", exception); | |||||
| } | |||||
| IsOpen = Handle.ToInt32() != NativeMethods.INVALID_HANDLE_VALUE; | |||||
| } | |||||
| public void CloseDevice() | |||||
| { | |||||
| if (!IsOpen) return; | |||||
| CloseDeviceIO(Handle); | |||||
| IsOpen = false; | |||||
| } | |||||
| public HidDeviceData Read() | |||||
| { | |||||
| return Read(0); | |||||
| } | |||||
| public HidDeviceData Read(int timeout) | |||||
| { | |||||
| if (IsConnected) | |||||
| { | |||||
| if (IsOpen == false) OpenDevice(_deviceReadMode, _deviceWriteMode, _deviceShareMode); | |||||
| try | |||||
| { | |||||
| return ReadData(timeout); | |||||
| } | |||||
| catch | |||||
| { | |||||
| return new HidDeviceData(HidDeviceData.ReadStatus.ReadError); | |||||
| } | |||||
| } | |||||
| return new HidDeviceData(HidDeviceData.ReadStatus.NotConnected); | |||||
| } | |||||
| public void Read(ReadCallback callback) | |||||
| { | |||||
| Read(callback, 0); | |||||
| } | |||||
| public void Read(ReadCallback callback, int timeout) | |||||
| { | |||||
| var readDelegate = new ReadDelegate(Read); | |||||
| var asyncState = new HidAsyncState(readDelegate, callback); | |||||
| readDelegate.BeginInvoke(timeout, EndRead, asyncState); | |||||
| } | |||||
| public async Task<HidDeviceData> ReadAsync(int timeout = 0) | |||||
| { | |||||
| var readDelegate = new ReadDelegate(Read); | |||||
| return await Task<HidDeviceData>.Factory.FromAsync(readDelegate.BeginInvoke, readDelegate.EndInvoke, timeout, null); | |||||
| } | |||||
| public HidReport ReadReport() | |||||
| { | |||||
| return ReadReport(0); | |||||
| } | |||||
| public HidReport ReadReport(int timeout) | |||||
| { | |||||
| return new HidReport(Capabilities.InputReportByteLength, Read(timeout)); | |||||
| } | |||||
| public void ReadReport(ReadReportCallback callback) | |||||
| { | |||||
| ReadReport(callback, 0); | |||||
| } | |||||
| public void ReadReport(ReadReportCallback callback, int timeout) | |||||
| { | |||||
| var readReportDelegate = new ReadReportDelegate(ReadReport); | |||||
| var asyncState = new HidAsyncState(readReportDelegate, callback); | |||||
| readReportDelegate.BeginInvoke(timeout, EndReadReport, asyncState); | |||||
| } | |||||
| public async Task<HidReport> ReadReportAsync(int timeout = 0) | |||||
| { | |||||
| var readReportDelegate = new ReadReportDelegate(ReadReport); | |||||
| return await Task<HidReport>.Factory.FromAsync(readReportDelegate.BeginInvoke, readReportDelegate.EndInvoke, timeout, null); | |||||
| } | |||||
| /// <summary> | |||||
| /// Reads an input report from the Control channel. This method provides access to report data for devices that | |||||
| /// do not use the interrupt channel to communicate for specific usages. | |||||
| /// </summary> | |||||
| /// <param name="reportId">The report ID to read from the device</param> | |||||
| /// <returns>The HID report that is read. The report will contain the success status of the read request</returns> | |||||
| /// | |||||
| public HidReport ReadReportSync(byte reportId) | |||||
| { | |||||
| byte[] cmdBuffer = new byte[Capabilities.InputReportByteLength]; | |||||
| cmdBuffer[0] = reportId; | |||||
| bool bSuccess = NativeMethods.HidD_GetInputReport(Handle, cmdBuffer, cmdBuffer.Length); | |||||
| HidDeviceData deviceData = new HidDeviceData(cmdBuffer, bSuccess ? HidDeviceData.ReadStatus.Success : HidDeviceData.ReadStatus.NoDataRead); | |||||
| return new HidReport(Capabilities.InputReportByteLength, deviceData); | |||||
| } | |||||
| public bool ReadFeatureData(out byte[] data, byte reportId = 0) | |||||
| { | |||||
| if (_deviceCapabilities.FeatureReportByteLength <= 0) | |||||
| { | |||||
| data = new byte[0]; | |||||
| return false; | |||||
| } | |||||
| data = new byte[_deviceCapabilities.FeatureReportByteLength]; | |||||
| var buffer = CreateFeatureOutputBuffer(); | |||||
| buffer[0] = reportId; | |||||
| IntPtr hidHandle = IntPtr.Zero; | |||||
| bool success = false; | |||||
| try | |||||
| { | |||||
| if (IsOpen) | |||||
| hidHandle = Handle; | |||||
| else | |||||
| hidHandle = OpenDeviceIO(_devicePath, NativeMethods.ACCESS_NONE); | |||||
| success = NativeMethods.HidD_GetFeature(hidHandle, buffer, buffer.Length); | |||||
| if (success) | |||||
| { | |||||
| Array.Copy(buffer, 0, data, 0, Math.Min(data.Length, _deviceCapabilities.FeatureReportByteLength)); | |||||
| } | |||||
| } | |||||
| catch (Exception exception) | |||||
| { | |||||
| throw new Exception(string.Format("Error accessing HID device '{0}'.", _devicePath), exception); | |||||
| } | |||||
| finally | |||||
| { | |||||
| if (hidHandle != IntPtr.Zero && hidHandle != Handle) | |||||
| CloseDeviceIO(hidHandle); | |||||
| } | |||||
| return success; | |||||
| } | |||||
| public bool ReadProduct(out byte[] data) | |||||
| { | |||||
| data = new byte[254]; | |||||
| IntPtr hidHandle = IntPtr.Zero; | |||||
| bool success = false; | |||||
| try | |||||
| { | |||||
| if (IsOpen) | |||||
| hidHandle = Handle; | |||||
| else | |||||
| hidHandle = OpenDeviceIO(_devicePath, NativeMethods.ACCESS_NONE); | |||||
| success = NativeMethods.HidD_GetProductString(hidHandle, ref data[0], data.Length); | |||||
| } | |||||
| catch (Exception exception) | |||||
| { | |||||
| throw new Exception(string.Format("Error accessing HID device '{0}'.", _devicePath), exception); | |||||
| } | |||||
| finally | |||||
| { | |||||
| if (hidHandle != IntPtr.Zero && hidHandle != Handle) | |||||
| CloseDeviceIO(hidHandle); | |||||
| } | |||||
| return success; | |||||
| } | |||||
| public bool ReadManufacturer(out byte[] data) | |||||
| { | |||||
| data = new byte[254]; | |||||
| IntPtr hidHandle = IntPtr.Zero; | |||||
| bool success = false; | |||||
| try | |||||
| { | |||||
| if (IsOpen) | |||||
| hidHandle = Handle; | |||||
| else | |||||
| hidHandle = OpenDeviceIO(_devicePath, NativeMethods.ACCESS_NONE); | |||||
| success = NativeMethods.HidD_GetManufacturerString(hidHandle, ref data[0], data.Length); | |||||
| } | |||||
| catch (Exception exception) | |||||
| { | |||||
| throw new Exception(string.Format("Error accessing HID device '{0}'.", _devicePath), exception); | |||||
| } | |||||
| finally | |||||
| { | |||||
| if (hidHandle != IntPtr.Zero && hidHandle != Handle) | |||||
| CloseDeviceIO(hidHandle); | |||||
| } | |||||
| return success; | |||||
| } | |||||
| public bool ReadSerialNumber(out byte[] data) | |||||
| { | |||||
| data = new byte[254]; | |||||
| IntPtr hidHandle = IntPtr.Zero; | |||||
| bool success = false; | |||||
| try | |||||
| { | |||||
| if (IsOpen) | |||||
| hidHandle = Handle; | |||||
| else | |||||
| hidHandle = OpenDeviceIO(_devicePath, NativeMethods.ACCESS_NONE); | |||||
| success = NativeMethods.HidD_GetSerialNumberString(hidHandle, ref data[0], data.Length); | |||||
| } | |||||
| catch (Exception exception) | |||||
| { | |||||
| throw new Exception(string.Format("Error accessing HID device '{0}'.", _devicePath), exception); | |||||
| } | |||||
| finally | |||||
| { | |||||
| if (hidHandle != IntPtr.Zero && hidHandle != Handle) | |||||
| CloseDeviceIO(hidHandle); | |||||
| } | |||||
| return success; | |||||
| } | |||||
| public bool Write(byte[] data) | |||||
| { | |||||
| return Write(data, 0); | |||||
| } | |||||
| public bool Write(byte[] data, int timeout) | |||||
| { | |||||
| if (IsConnected) | |||||
| { | |||||
| if (IsOpen == false) OpenDevice(_deviceReadMode, _deviceWriteMode, _deviceShareMode); | |||||
| try | |||||
| { | |||||
| return WriteData(data, timeout); | |||||
| } | |||||
| catch | |||||
| { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| public void Write(byte[] data, WriteCallback callback) | |||||
| { | |||||
| Write(data, callback, 0); | |||||
| } | |||||
| public void Write(byte[] data, WriteCallback callback, int timeout) | |||||
| { | |||||
| var writeDelegate = new WriteDelegate(Write); | |||||
| var asyncState = new HidAsyncState(writeDelegate, callback); | |||||
| writeDelegate.BeginInvoke(data, timeout, EndWrite, asyncState); | |||||
| } | |||||
| public async Task<bool> WriteAsync(byte[] data, int timeout = 0) | |||||
| { | |||||
| var writeDelegate = new WriteDelegate(Write); | |||||
| return await Task<bool>.Factory.FromAsync(writeDelegate.BeginInvoke, writeDelegate.EndInvoke, data, timeout, null); | |||||
| } | |||||
| public bool WriteReport(HidReport report) | |||||
| { | |||||
| return WriteReport(report, 0); | |||||
| } | |||||
| public bool WriteReport(HidReport report, int timeout) | |||||
| { | |||||
| return Write(report.GetBytes(), timeout); | |||||
| } | |||||
| public void WriteReport(HidReport report, WriteCallback callback) | |||||
| { | |||||
| WriteReport(report, callback, 0); | |||||
| } | |||||
| public void WriteReport(HidReport report, WriteCallback callback, int timeout) | |||||
| { | |||||
| var writeReportDelegate = new WriteReportDelegate(WriteReport); | |||||
| var asyncState = new HidAsyncState(writeReportDelegate, callback); | |||||
| writeReportDelegate.BeginInvoke(report, timeout, EndWriteReport, asyncState); | |||||
| } | |||||
| /// <summary> | |||||
| /// Handle data transfers on the control channel. This method places data on the control channel for devices | |||||
| /// that do not support the interupt transfers | |||||
| /// </summary> | |||||
| /// <param name="report">The outbound HID report</param> | |||||
| /// <returns>The result of the tranfer request: true if successful otherwise false</returns> | |||||
| /// | |||||
| public bool WriteReportSync(HidReport report) | |||||
| { | |||||
| if (null != report) | |||||
| { | |||||
| byte[] buffer = report.GetBytes(); | |||||
| return (NativeMethods.HidD_SetOutputReport(Handle, buffer, buffer.Length)); | |||||
| } | |||||
| else | |||||
| throw new ArgumentException("The output report is null, it must be allocated before you call this method", "report"); | |||||
| } | |||||
| public async Task<bool> WriteReportAsync(HidReport report, int timeout = 0) | |||||
| { | |||||
| var writeReportDelegate = new WriteReportDelegate(WriteReport); | |||||
| return await Task<bool>.Factory.FromAsync(writeReportDelegate.BeginInvoke, writeReportDelegate.EndInvoke, report, timeout, null); | |||||
| } | |||||
| public HidReport CreateReport() | |||||
| { | |||||
| return new HidReport(Capabilities.OutputReportByteLength); | |||||
| } | |||||
| public bool WriteFeatureData(byte[] data) | |||||
| { | |||||
| if (_deviceCapabilities.FeatureReportByteLength <= 0) return false; | |||||
| var buffer = CreateFeatureOutputBuffer(); | |||||
| Array.Copy(data, 0, buffer, 0, Math.Min(data.Length, _deviceCapabilities.FeatureReportByteLength)); | |||||
| IntPtr hidHandle = IntPtr.Zero; | |||||
| bool success = false; | |||||
| try | |||||
| { | |||||
| if (IsOpen) | |||||
| hidHandle = Handle; | |||||
| else | |||||
| hidHandle = OpenDeviceIO(_devicePath, NativeMethods.ACCESS_NONE); | |||||
| //var overlapped = new NativeOverlapped(); | |||||
| success = NativeMethods.HidD_SetFeature(hidHandle, buffer, buffer.Length); | |||||
| } | |||||
| catch (Exception exception) | |||||
| { | |||||
| throw new Exception(string.Format("Error accessing HID device '{0}'.", _devicePath), exception); | |||||
| } | |||||
| finally | |||||
| { | |||||
| if (hidHandle != IntPtr.Zero && hidHandle != Handle) | |||||
| CloseDeviceIO(hidHandle); | |||||
| } | |||||
| return success; | |||||
| } | |||||
| protected static void EndRead(IAsyncResult ar) | |||||
| { | |||||
| var hidAsyncState = (HidAsyncState)ar.AsyncState; | |||||
| var callerDelegate = (ReadDelegate)hidAsyncState.CallerDelegate; | |||||
| var callbackDelegate = (ReadCallback)hidAsyncState.CallbackDelegate; | |||||
| var data = callerDelegate.EndInvoke(ar); | |||||
| if ((callbackDelegate != null)) callbackDelegate.Invoke(data); | |||||
| } | |||||
| protected static void EndReadReport(IAsyncResult ar) | |||||
| { | |||||
| var hidAsyncState = (HidAsyncState)ar.AsyncState; | |||||
| var callerDelegate = (ReadReportDelegate)hidAsyncState.CallerDelegate; | |||||
| var callbackDelegate = (ReadReportCallback)hidAsyncState.CallbackDelegate; | |||||
| var report = callerDelegate.EndInvoke(ar); | |||||
| if ((callbackDelegate != null)) callbackDelegate.Invoke(report); | |||||
| } | |||||
| private static void EndWrite(IAsyncResult ar) | |||||
| { | |||||
| var hidAsyncState = (HidAsyncState)ar.AsyncState; | |||||
| var callerDelegate = (WriteDelegate)hidAsyncState.CallerDelegate; | |||||
| var callbackDelegate = (WriteCallback)hidAsyncState.CallbackDelegate; | |||||
| var result = callerDelegate.EndInvoke(ar); | |||||
| if ((callbackDelegate != null)) callbackDelegate.Invoke(result); | |||||
| } | |||||
| private static void EndWriteReport(IAsyncResult ar) | |||||
| { | |||||
| var hidAsyncState = (HidAsyncState)ar.AsyncState; | |||||
| var callerDelegate = (WriteReportDelegate)hidAsyncState.CallerDelegate; | |||||
| var callbackDelegate = (WriteCallback)hidAsyncState.CallbackDelegate; | |||||
| var result = callerDelegate.EndInvoke(ar); | |||||
| if ((callbackDelegate != null)) callbackDelegate.Invoke(result); | |||||
| } | |||||
| 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[] buffer = null; | |||||
| Array.Resize(ref buffer, length + 1); | |||||
| return buffer; | |||||
| } | |||||
| private static HidDeviceAttributes GetDeviceAttributes(IntPtr hidHandle) | |||||
| { | |||||
| var deviceAttributes = default(NativeMethods.HIDD_ATTRIBUTES); | |||||
| deviceAttributes.Size = Marshal.SizeOf(deviceAttributes); | |||||
| NativeMethods.HidD_GetAttributes(hidHandle, ref deviceAttributes); | |||||
| return new HidDeviceAttributes(deviceAttributes); | |||||
| } | |||||
| private static HidDeviceCapabilities GetDeviceCapabilities(IntPtr hidHandle) | |||||
| { | |||||
| var capabilities = default(NativeMethods.HIDP_CAPS); | |||||
| var preparsedDataPointer = default(IntPtr); | |||||
| if (NativeMethods.HidD_GetPreparsedData(hidHandle, ref preparsedDataPointer)) | |||||
| { | |||||
| NativeMethods.HidP_GetCaps(preparsedDataPointer, ref capabilities); | |||||
| NativeMethods.HidD_FreePreparsedData(preparsedDataPointer); | |||||
| } | |||||
| return new HidDeviceCapabilities(capabilities); | |||||
| } | |||||
| private bool WriteData(byte[] data, int timeout) | |||||
| { | |||||
| if (_deviceCapabilities.OutputReportByteLength <= 0) return false; | |||||
| var buffer = CreateOutputBuffer(); | |||||
| uint bytesWritten = 0; | |||||
| Array.Copy(data, 0, buffer, 0, Math.Min(data.Length, _deviceCapabilities.OutputReportByteLength)); | |||||
| if (_deviceWriteMode == DeviceMode.Overlapped) | |||||
| { | |||||
| var security = new NativeMethods.SECURITY_ATTRIBUTES(); | |||||
| var overlapped = new NativeOverlapped(); | |||||
| var overlapTimeout = timeout <= 0 ? NativeMethods.WAIT_INFINITE : timeout; | |||||
| security.lpSecurityDescriptor = IntPtr.Zero; | |||||
| security.bInheritHandle = true; | |||||
| security.nLength = Marshal.SizeOf(security); | |||||
| overlapped.OffsetLow = 0; | |||||
| overlapped.OffsetHigh = 0; | |||||
| overlapped.EventHandle = NativeMethods.CreateEvent(ref security, Convert.ToInt32(false), Convert.ToInt32(true), ""); | |||||
| try | |||||
| { | |||||
| NativeMethods.WriteFile(Handle, buffer, (uint)buffer.Length, out bytesWritten, ref overlapped); | |||||
| } | |||||
| catch { return false; } | |||||
| var result = NativeMethods.WaitForSingleObject(overlapped.EventHandle, overlapTimeout); | |||||
| switch (result) | |||||
| { | |||||
| case NativeMethods.WAIT_OBJECT_0: | |||||
| return true; | |||||
| case NativeMethods.WAIT_TIMEOUT: | |||||
| return false; | |||||
| case NativeMethods.WAIT_FAILED: | |||||
| return false; | |||||
| default: | |||||
| return false; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| try | |||||
| { | |||||
| var overlapped = new NativeOverlapped(); | |||||
| return NativeMethods.WriteFile(Handle, buffer, (uint)buffer.Length, out bytesWritten, ref overlapped); | |||||
| } | |||||
| catch { return false; } | |||||
| } | |||||
| } | |||||
| protected HidDeviceData ReadData(int timeout) | |||||
| { | |||||
| var buffer = new byte[] { }; | |||||
| var status = HidDeviceData.ReadStatus.NoDataRead; | |||||
| IntPtr nonManagedBuffer; | |||||
| if (_deviceCapabilities.InputReportByteLength > 0) | |||||
| { | |||||
| uint bytesRead = 0; | |||||
| buffer = CreateInputBuffer(); | |||||
| nonManagedBuffer = Marshal.AllocHGlobal(buffer.Length); | |||||
| if (_deviceReadMode == DeviceMode.Overlapped) | |||||
| { | |||||
| var security = new NativeMethods.SECURITY_ATTRIBUTES(); | |||||
| var overlapped = new NativeOverlapped(); | |||||
| var overlapTimeout = timeout <= 0 ? NativeMethods.WAIT_INFINITE : timeout; | |||||
| security.lpSecurityDescriptor = IntPtr.Zero; | |||||
| security.bInheritHandle = true; | |||||
| security.nLength = Marshal.SizeOf(security); | |||||
| overlapped.OffsetLow = 0; | |||||
| overlapped.OffsetHigh = 0; | |||||
| overlapped.EventHandle = NativeMethods.CreateEvent(ref security, Convert.ToInt32(false), Convert.ToInt32(true), string.Empty); | |||||
| try | |||||
| { | |||||
| var success = NativeMethods.ReadFile(Handle, nonManagedBuffer, (uint)buffer.Length, out bytesRead, ref overlapped); | |||||
| if (!success) { | |||||
| var result = NativeMethods.WaitForSingleObject(overlapped.EventHandle, overlapTimeout); | |||||
| switch (result) | |||||
| { | |||||
| case NativeMethods.WAIT_OBJECT_0: | |||||
| status = HidDeviceData.ReadStatus.Success; | |||||
| NativeMethods.GetOverlappedResult(Handle, ref overlapped, out bytesRead, false); | |||||
| break; | |||||
| case NativeMethods.WAIT_TIMEOUT: | |||||
| status = HidDeviceData.ReadStatus.WaitTimedOut; | |||||
| buffer = new byte[] { }; | |||||
| break; | |||||
| case NativeMethods.WAIT_FAILED: | |||||
| status = HidDeviceData.ReadStatus.WaitFail; | |||||
| buffer = new byte[] { }; | |||||
| break; | |||||
| default: | |||||
| status = HidDeviceData.ReadStatus.NoDataRead; | |||||
| buffer = new byte[] { }; | |||||
| break; | |||||
| } | |||||
| } | |||||
| Marshal.Copy(nonManagedBuffer, buffer, 0, (int)bytesRead); | |||||
| } | |||||
| catch { status = HidDeviceData.ReadStatus.ReadError; } | |||||
| finally { | |||||
| CloseDeviceIO(overlapped.EventHandle); | |||||
| Marshal.FreeHGlobal(nonManagedBuffer); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| try | |||||
| { | |||||
| var overlapped = new NativeOverlapped(); | |||||
| NativeMethods.ReadFile(Handle, nonManagedBuffer, (uint)buffer.Length, out bytesRead, ref overlapped); | |||||
| status = HidDeviceData.ReadStatus.Success; | |||||
| Marshal.Copy(nonManagedBuffer, buffer, 0, (int)bytesRead); | |||||
| } | |||||
| catch { status = HidDeviceData.ReadStatus.ReadError; } | |||||
| finally { Marshal.FreeHGlobal(nonManagedBuffer); } | |||||
| } | |||||
| } | |||||
| return new HidDeviceData(buffer, status); | |||||
| } | |||||
| private static IntPtr OpenDeviceIO(string devicePath, uint deviceAccess) | |||||
| { | |||||
| return OpenDeviceIO(devicePath, DeviceMode.NonOverlapped, deviceAccess, ShareMode.ShareRead | ShareMode.ShareWrite); | |||||
| } | |||||
| private static IntPtr OpenDeviceIO(string devicePath, DeviceMode deviceMode, uint deviceAccess, ShareMode shareMode) | |||||
| { | |||||
| var security = new NativeMethods.SECURITY_ATTRIBUTES(); | |||||
| var flags = 0; | |||||
| if (deviceMode == DeviceMode.Overlapped) flags = NativeMethods.FILE_FLAG_OVERLAPPED; | |||||
| security.lpSecurityDescriptor = IntPtr.Zero; | |||||
| security.bInheritHandle = true; | |||||
| security.nLength = Marshal.SizeOf(security); | |||||
| return NativeMethods.CreateFile(devicePath, deviceAccess, (int)shareMode, ref security, NativeMethods.OPEN_EXISTING, flags, 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(_deviceReadMode, _deviceWriteMode, _deviceShareMode); | |||||
| if (Inserted != null) Inserted(); | |||||
| } | |||||
| private void DeviceEventMonitorRemoved() | |||||
| { | |||||
| if (IsOpen) CloseDevice(); | |||||
| if (Removed != null) Removed(); | |||||
| } | |||||
| public void Dispose() | |||||
| { | |||||
| if (MonitorDeviceEvents) MonitorDeviceEvents = false; | |||||
| if (IsOpen) CloseDevice(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,21 @@ | |||||
| namespace HidLibrary | |||||
| { | |||||
| public class HidDeviceAttributes | |||||
| { | |||||
| internal HidDeviceAttributes(NativeMethods.HIDD_ATTRIBUTES attributes) | |||||
| { | |||||
| VendorId = attributes.VendorID; | |||||
| ProductId = attributes.ProductID; | |||||
| Version = attributes.VersionNumber; | |||||
| VendorHexId = "0x" + attributes.VendorID.ToString("X4"); | |||||
| ProductHexId = "0x" + attributes.ProductID.ToString("X4"); | |||||
| } | |||||
| public int VendorId { get; private set; } | |||||
| public int ProductId { get; private set; } | |||||
| public int Version { get; private set; } | |||||
| public string VendorHexId { get; set; } | |||||
| public string ProductHexId { get; set; } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,43 @@ | |||||
| namespace HidLibrary | |||||
| { | |||||
| public class HidDeviceCapabilities | |||||
| { | |||||
| internal HidDeviceCapabilities(NativeMethods.HIDP_CAPS capabilities) | |||||
| { | |||||
| Usage = capabilities.Usage; | |||||
| UsagePage = capabilities.UsagePage; | |||||
| InputReportByteLength = capabilities.InputReportByteLength; | |||||
| OutputReportByteLength = capabilities.OutputReportByteLength; | |||||
| FeatureReportByteLength = capabilities.FeatureReportByteLength; | |||||
| Reserved = capabilities.Reserved; | |||||
| NumberLinkCollectionNodes = capabilities.NumberLinkCollectionNodes; | |||||
| NumberInputButtonCaps = capabilities.NumberInputButtonCaps; | |||||
| NumberInputValueCaps = capabilities.NumberInputValueCaps; | |||||
| NumberInputDataIndices = capabilities.NumberInputDataIndices; | |||||
| NumberOutputButtonCaps = capabilities.NumberOutputButtonCaps; | |||||
| NumberOutputValueCaps = capabilities.NumberOutputValueCaps; | |||||
| NumberOutputDataIndices = capabilities.NumberOutputDataIndices; | |||||
| NumberFeatureButtonCaps = capabilities.NumberFeatureButtonCaps; | |||||
| NumberFeatureValueCaps = capabilities.NumberFeatureValueCaps; | |||||
| NumberFeatureDataIndices = capabilities.NumberFeatureDataIndices; | |||||
| } | |||||
| public short Usage { get; private set; } | |||||
| public short UsagePage { get; private set; } | |||||
| public short InputReportByteLength { get; private set; } | |||||
| public short OutputReportByteLength { get; private set; } | |||||
| public short FeatureReportByteLength { get; private set; } | |||||
| public short[] Reserved { get; private set; } | |||||
| public short NumberLinkCollectionNodes { get; private set; } | |||||
| public short NumberInputButtonCaps { get; private set; } | |||||
| public short NumberInputValueCaps { get; private set; } | |||||
| public short NumberInputDataIndices { get; private set; } | |||||
| public short NumberOutputButtonCaps { get; private set; } | |||||
| public short NumberOutputValueCaps { get; private set; } | |||||
| public short NumberOutputDataIndices { get; private set; } | |||||
| public short NumberFeatureButtonCaps { get; private set; } | |||||
| public short NumberFeatureValueCaps { get; private set; } | |||||
| public short NumberFeatureDataIndices { get; private set; } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,30 @@ | |||||
| namespace HidLibrary | |||||
| { | |||||
| public class HidDeviceData | |||||
| { | |||||
| public enum ReadStatus | |||||
| { | |||||
| Success = 0, | |||||
| WaitTimedOut = 1, | |||||
| WaitFail = 2, | |||||
| NoDataRead = 3, | |||||
| ReadError = 4, | |||||
| NotConnected = 5 | |||||
| } | |||||
| public HidDeviceData(ReadStatus status) | |||||
| { | |||||
| Data = new byte[] {}; | |||||
| Status = status; | |||||
| } | |||||
| public HidDeviceData(byte[] data, ReadStatus status) | |||||
| { | |||||
| Data = data; | |||||
| Status = status; | |||||
| } | |||||
| public byte[] Data { get; private set; } | |||||
| public ReadStatus Status { get; private set; } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,49 @@ | |||||
| using System; | |||||
| using System.Threading; | |||||
| namespace HidLibrary | |||||
| { | |||||
| internal class HidDeviceEventMonitor | |||||
| { | |||||
| public event InsertedEventHandler Inserted; | |||||
| public event RemovedEventHandler Removed; | |||||
| public delegate void InsertedEventHandler(); | |||||
| public delegate void RemovedEventHandler(); | |||||
| private readonly HidDevice _device; | |||||
| private bool _wasConnected; | |||||
| public HidDeviceEventMonitor(HidDevice device) | |||||
| { | |||||
| _device = device; | |||||
| } | |||||
| public void Init() | |||||
| { | |||||
| var eventMonitor = new Action(DeviceEventMonitor); | |||||
| eventMonitor.BeginInvoke(DisposeDeviceEventMonitor, eventMonitor); | |||||
| } | |||||
| private void DeviceEventMonitor() | |||||
| { | |||||
| var isConnected = _device.IsConnected; | |||||
| if (isConnected != _wasConnected) | |||||
| { | |||||
| if (isConnected && Inserted != null) Inserted(); | |||||
| else if (!isConnected && Removed != null) Removed(); | |||||
| _wasConnected = isConnected; | |||||
| } | |||||
| Thread.Sleep(500); | |||||
| if (_device.MonitorDeviceEvents) Init(); | |||||
| } | |||||
| private static void DisposeDeviceEventMonitor(IAsyncResult ar) | |||||
| { | |||||
| ((Action)ar.AsyncState).EndInvoke(ar); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,157 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Diagnostics; | |||||
| using System.Linq; | |||||
| using System.Runtime.InteropServices; | |||||
| namespace HidLibrary | |||||
| { | |||||
| public class HidDevices | |||||
| { | |||||
| private static Guid _hidClassGuid = Guid.Empty; | |||||
| public static bool IsConnected(string devicePath) | |||||
| { | |||||
| return EnumerateDevices().Any(x => x.Path == devicePath); | |||||
| } | |||||
| public static HidDevice GetDevice(string devicePath) | |||||
| { | |||||
| return Enumerate(devicePath).FirstOrDefault(); | |||||
| } | |||||
| public static IEnumerable<HidDevice> Enumerate() | |||||
| { | |||||
| return EnumerateDevices().Select(x => new HidDevice(x.Path, x.Description)); | |||||
| } | |||||
| public static IEnumerable<HidDevice> Enumerate(string devicePath) | |||||
| { | |||||
| return EnumerateDevices().Where(x => x.Path == devicePath).Select(x => new HidDevice(x.Path, x.Description)); | |||||
| } | |||||
| public static IEnumerable<HidDevice> Enumerate(int vendorId, params int[] productIds) | |||||
| { | |||||
| // var all = EnumerateDevices(); | |||||
| return EnumerateDevices().Select(x => new HidDevice(x.Path, x.Description)).Where(x => x.Attributes.VendorId == vendorId && productIds.Contains(x.Attributes.ProductId)); | |||||
| } | |||||
| public static IEnumerable<HidDevice> Enumerate(int vendorId, int productId, ushort UsagePage) | |||||
| { | |||||
| return EnumerateDevices().Select(x => new HidDevice(x.Path, x.Description)).Where(x => x.Attributes.VendorId == vendorId && productId == (ushort)x.Attributes.ProductId && (ushort)x.Capabilities.UsagePage == UsagePage); | |||||
| } | |||||
| public static IEnumerable<HidDevice> Enumerate(int vendorId) | |||||
| { | |||||
| return EnumerateDevices().Select(x => new HidDevice(x.Path, x.Description)).Where(x => x.Attributes.VendorId == vendorId); | |||||
| } | |||||
| internal class DeviceInfo { public string Path { get; set; } public string Description { get; set; } } | |||||
| internal static IEnumerable<DeviceInfo> EnumerateDevices() | |||||
| { | |||||
| var devices = new List<DeviceInfo>(); | |||||
| var hidClass = HidClassGuid; | |||||
| var deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref hidClass, null, 0, NativeMethods.DIGCF_PRESENT | NativeMethods.DIGCF_DEVICEINTERFACE); | |||||
| if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) | |||||
| { | |||||
| var deviceInfoData = CreateDeviceInfoData(); | |||||
| var deviceIndex = 0; | |||||
| while (NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, deviceIndex, ref deviceInfoData)) | |||||
| { | |||||
| deviceIndex += 1; | |||||
| var deviceInterfaceData = new NativeMethods.SP_DEVICE_INTERFACE_DATA(); | |||||
| deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData); | |||||
| var deviceInterfaceIndex = 0; | |||||
| while (NativeMethods.SetupDiEnumDeviceInterfaces(deviceInfoSet, ref deviceInfoData, ref hidClass, deviceInterfaceIndex, ref deviceInterfaceData)) | |||||
| { | |||||
| deviceInterfaceIndex++; | |||||
| var devicePath = GetDevicePath(deviceInfoSet, deviceInterfaceData); | |||||
| var description = GetBusReportedDeviceDescription(deviceInfoSet, ref deviceInfoData) ?? | |||||
| GetDeviceDescription(deviceInfoSet, ref deviceInfoData); | |||||
| devices.Add(new DeviceInfo { Path = devicePath, Description = description }); | |||||
| } | |||||
| } | |||||
| NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); | |||||
| } | |||||
| return devices; | |||||
| } | |||||
| private static NativeMethods.SP_DEVINFO_DATA CreateDeviceInfoData() | |||||
| { | |||||
| var deviceInfoData = new NativeMethods.SP_DEVINFO_DATA(); | |||||
| deviceInfoData.cbSize = Marshal.SizeOf(deviceInfoData); | |||||
| deviceInfoData.DevInst = 0; | |||||
| deviceInfoData.ClassGuid = Guid.Empty; | |||||
| deviceInfoData.Reserved = IntPtr.Zero; | |||||
| return deviceInfoData; | |||||
| } | |||||
| private static string GetDevicePath(IntPtr deviceInfoSet, NativeMethods.SP_DEVICE_INTERFACE_DATA deviceInterfaceData) | |||||
| { | |||||
| var bufferSize = 0; | |||||
| var interfaceDetail = new NativeMethods.SP_DEVICE_INTERFACE_DETAIL_DATA { Size = IntPtr.Size == 4 ? 4 + Marshal.SystemDefaultCharSize : 8 }; | |||||
| NativeMethods.SetupDiGetDeviceInterfaceDetailBuffer(deviceInfoSet, ref deviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, IntPtr.Zero); | |||||
| return NativeMethods.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, ref interfaceDetail, bufferSize, ref bufferSize, IntPtr.Zero) ? | |||||
| interfaceDetail.DevicePath : null; | |||||
| } | |||||
| private static Guid HidClassGuid | |||||
| { | |||||
| get | |||||
| { | |||||
| if (_hidClassGuid.Equals(Guid.Empty)) NativeMethods.HidD_GetHidGuid(ref _hidClassGuid); | |||||
| return _hidClassGuid; | |||||
| } | |||||
| } | |||||
| private static string GetDeviceDescription(IntPtr deviceInfoSet, ref NativeMethods.SP_DEVINFO_DATA devinfoData) | |||||
| { | |||||
| var descriptionBuffer = new byte[1024]; | |||||
| var requiredSize = 0; | |||||
| var type = 0; | |||||
| NativeMethods.SetupDiGetDeviceRegistryProperty(deviceInfoSet, | |||||
| ref devinfoData, | |||||
| NativeMethods.SPDRP_DEVICEDESC, | |||||
| ref type, | |||||
| descriptionBuffer, | |||||
| descriptionBuffer.Length, | |||||
| ref requiredSize); | |||||
| return descriptionBuffer.ToUTF8String(); | |||||
| } | |||||
| private static string GetBusReportedDeviceDescription(IntPtr deviceInfoSet, ref NativeMethods.SP_DEVINFO_DATA devinfoData) | |||||
| { | |||||
| var descriptionBuffer = new byte[1024]; | |||||
| if (Environment.OSVersion.Version.Major > 5) | |||||
| { | |||||
| ulong propertyType = 0; | |||||
| var requiredSize = 0; | |||||
| var _continue = NativeMethods.SetupDiGetDeviceProperty(deviceInfoSet, | |||||
| ref devinfoData, | |||||
| ref NativeMethods.DEVPKEY_Device_BusReportedDeviceDesc, | |||||
| ref propertyType, | |||||
| descriptionBuffer, | |||||
| descriptionBuffer.Length, | |||||
| ref requiredSize, | |||||
| 0); | |||||
| if (_continue) return descriptionBuffer.ToUTF16String(); | |||||
| } | |||||
| return null; | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,76 @@ | |||||
| using System.Threading.Tasks; | |||||
| namespace HidLibrary | |||||
| { | |||||
| public class HidFastReadDevice : HidDevice | |||||
| { | |||||
| internal HidFastReadDevice(string devicePath, string description = null) | |||||
| : base(devicePath, description) { } | |||||
| // FastRead assumes that the device is connected, | |||||
| // which could cause stability issues if hardware is | |||||
| // disconnected during a read | |||||
| public HidDeviceData FastRead() | |||||
| { | |||||
| return FastRead(0); | |||||
| } | |||||
| public HidDeviceData FastRead(int timeout) | |||||
| { | |||||
| try | |||||
| { | |||||
| return ReadData(timeout); | |||||
| } | |||||
| catch | |||||
| { | |||||
| return new HidDeviceData(HidDeviceData.ReadStatus.ReadError); | |||||
| } | |||||
| } | |||||
| public void FastRead(ReadCallback callback) | |||||
| { | |||||
| FastRead(callback, 0); | |||||
| } | |||||
| public void FastRead(ReadCallback callback, int timeout) | |||||
| { | |||||
| var readDelegate = new ReadDelegate(FastRead); | |||||
| var asyncState = new HidAsyncState(readDelegate, callback); | |||||
| readDelegate.BeginInvoke(timeout, EndRead, asyncState); | |||||
| } | |||||
| public async Task<HidDeviceData> FastReadAsync(int timeout = 0) | |||||
| { | |||||
| var readDelegate = new ReadDelegate(FastRead); | |||||
| return await Task<HidDeviceData>.Factory.FromAsync(readDelegate.BeginInvoke, readDelegate.EndInvoke, timeout, null); | |||||
| } | |||||
| public HidReport FastReadReport() | |||||
| { | |||||
| return FastReadReport(0); | |||||
| } | |||||
| public HidReport FastReadReport(int timeout) | |||||
| { | |||||
| return new HidReport(Capabilities.InputReportByteLength, FastRead(timeout)); | |||||
| } | |||||
| public void FastReadReport(ReadReportCallback callback) | |||||
| { | |||||
| FastReadReport(callback, 0); | |||||
| } | |||||
| public void FastReadReport(ReadReportCallback callback, int timeout) | |||||
| { | |||||
| var readReportDelegate = new ReadReportDelegate(FastReadReport); | |||||
| var asyncState = new HidAsyncState(readReportDelegate, callback); | |||||
| readReportDelegate.BeginInvoke(timeout, EndReadReport, asyncState); | |||||
| } | |||||
| public async Task<HidReport> FastReadReportAsync(int timeout = 0) | |||||
| { | |||||
| var readReportDelegate = new ReadReportDelegate(FastReadReport); | |||||
| return await Task<HidReport>.Factory.FromAsync(readReportDelegate.BeginInvoke, readReportDelegate.EndInvoke, timeout, null); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,44 @@ | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| namespace HidLibrary | |||||
| { | |||||
| public class HidFastReadEnumerator : IHidEnumerator | |||||
| { | |||||
| public bool IsConnected(string devicePath) | |||||
| { | |||||
| return HidDevices.IsConnected(devicePath); | |||||
| } | |||||
| public IHidDevice GetDevice(string devicePath) | |||||
| { | |||||
| return Enumerate(devicePath).FirstOrDefault() as IHidDevice; | |||||
| } | |||||
| public IEnumerable<IHidDevice> Enumerate() | |||||
| { | |||||
| return HidDevices.EnumerateDevices(). | |||||
| Select(d => new HidFastReadDevice(d.Path, d.Description) as IHidDevice); | |||||
| } | |||||
| public IEnumerable<IHidDevice> Enumerate(string devicePath) | |||||
| { | |||||
| return HidDevices.EnumerateDevices().Where(x => x.Path == devicePath). | |||||
| Select(d => new HidFastReadDevice(d.Path, d.Description) as IHidDevice); | |||||
| } | |||||
| public IEnumerable<IHidDevice> Enumerate(int vendorId, params int[] productIds) | |||||
| { | |||||
| return HidDevices.EnumerateDevices().Select(d => new HidFastReadDevice(d.Path, d.Description)). | |||||
| Where(f => f.Attributes.VendorId == vendorId && productIds.Contains(f.Attributes.ProductId)). | |||||
| Select(d => d as IHidDevice); | |||||
| } | |||||
| public IEnumerable<IHidDevice> Enumerate(int vendorId) | |||||
| { | |||||
| return HidDevices.EnumerateDevices().Select(d => new HidFastReadDevice(d.Path, d.Description)). | |||||
| Where(f => f.Attributes.VendorId == vendorId). | |||||
| Select(d => d as IHidDevice); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,71 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
| <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | |||||
| <PropertyGroup> | |||||
| <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||||
| <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||||
| <ProjectGuid>{41D547F1-D57D-4D50-9602-6EE65CA958B2}</ProjectGuid> | |||||
| <OutputType>Library</OutputType> | |||||
| <AppDesignerFolder>Properties</AppDesignerFolder> | |||||
| <RootNamespace>HidLibrary</RootNamespace> | |||||
| <AssemblyName>HidLibrary</AssemblyName> | |||||
| <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> | |||||
| <FileAlignment>512</FileAlignment> | |||||
| <TargetFrameworkProfile /> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||||
| <DebugSymbols>true</DebugSymbols> | |||||
| <DebugType>full</DebugType> | |||||
| <Optimize>false</Optimize> | |||||
| <OutputPath>bin\Debug\</OutputPath> | |||||
| <DefineConstants>DEBUG;TRACE</DefineConstants> | |||||
| <ErrorReport>prompt</ErrorReport> | |||||
| <WarningLevel>4</WarningLevel> | |||||
| <Prefer32Bit>false</Prefer32Bit> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||||
| <DebugType>pdbonly</DebugType> | |||||
| <Optimize>true</Optimize> | |||||
| <OutputPath>bin\Release\</OutputPath> | |||||
| <DefineConstants>TRACE</DefineConstants> | |||||
| <ErrorReport>prompt</ErrorReport> | |||||
| <WarningLevel>4</WarningLevel> | |||||
| <Prefer32Bit>false</Prefer32Bit> | |||||
| </PropertyGroup> | |||||
| <ItemGroup> | |||||
| <Reference Include="System" /> | |||||
| <Reference Include="System.Core" /> | |||||
| <Reference Include="System.Xml.Linq" /> | |||||
| <Reference Include="System.Data.DataSetExtensions" /> | |||||
| <Reference Include="Microsoft.CSharp" /> | |||||
| <Reference Include="System.Data" /> | |||||
| <Reference Include="System.Xml" /> | |||||
| </ItemGroup> | |||||
| <ItemGroup> | |||||
| <Compile Include="Extensions.cs" /> | |||||
| <Compile Include="HidAsyncState.cs" /> | |||||
| <Compile Include="HidDevice.cs" /> | |||||
| <Compile Include="HidDeviceAttributes.cs" /> | |||||
| <Compile Include="HidDeviceCapabilities.cs" /> | |||||
| <Compile Include="HidDeviceData.cs" /> | |||||
| <Compile Include="HidDeviceEventMonitor.cs" /> | |||||
| <Compile Include="HidDevices.cs" /> | |||||
| <Compile Include="HidFastReadDevice.cs" /> | |||||
| <Compile Include="HidFastReadEnumerator.cs" /> | |||||
| <Compile Include="HidReport.cs" /> | |||||
| <Compile Include="Honeywell1902Series\Report1902.cs" /> | |||||
| <Compile Include="Honeywell1902Series\Scanner1902.cs" /> | |||||
| <Compile Include="IHidDevice.cs" /> | |||||
| <Compile Include="IHidEnumerator.cs" /> | |||||
| <Compile Include="NativeMethods.cs" /> | |||||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||||
| </ItemGroup> | |||||
| <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||||
| <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||||
| Other similar extension points exist, see Microsoft.Common.targets. | |||||
| <Target Name="BeforeBuild"> | |||||
| </Target> | |||||
| <Target Name="AfterBuild"> | |||||
| </Target> | |||||
| --> | |||||
| </Project> | |||||
| @ -0,0 +1,75 @@ | |||||
| using System; | |||||
| namespace HidLibrary | |||||
| { | |||||
| public class HidReport | |||||
| { | |||||
| private byte _reportId; | |||||
| private byte[] _data = new byte[] {}; | |||||
| private readonly HidDeviceData.ReadStatus _status; | |||||
| public HidReport(int reportSize) | |||||
| { | |||||
| Array.Resize(ref _data, reportSize - 1); | |||||
| } | |||||
| public HidReport(int reportSize, HidDeviceData deviceData) | |||||
| { | |||||
| _status = deviceData.Status; | |||||
| Array.Resize(ref _data, reportSize - 1); | |||||
| if ((deviceData.Data != null)) | |||||
| { | |||||
| if (deviceData.Data.Length > 0) | |||||
| { | |||||
| _reportId = deviceData.Data[0]; | |||||
| Exists = true; | |||||
| if (deviceData.Data.Length > 1) | |||||
| { | |||||
| var dataLength = reportSize - 1; | |||||
| if (deviceData.Data.Length < reportSize - 1) dataLength = deviceData.Data.Length; | |||||
| Array.Copy(deviceData.Data, 1, _data, 0, dataLength); | |||||
| } | |||||
| } | |||||
| else Exists = false; | |||||
| } | |||||
| else Exists = false; | |||||
| } | |||||
| public bool Exists { get; private set; } | |||||
| public HidDeviceData.ReadStatus ReadStatus { get { return _status; } } | |||||
| public byte ReportId | |||||
| { | |||||
| get { return _reportId; } | |||||
| set | |||||
| { | |||||
| _reportId = value; | |||||
| Exists = true; | |||||
| } | |||||
| } | |||||
| public byte[] Data | |||||
| { | |||||
| get { return _data; } | |||||
| set | |||||
| { | |||||
| _data = value; | |||||
| Exists = true; | |||||
| } | |||||
| } | |||||
| public byte[] GetBytes() | |||||
| { | |||||
| byte[] data = null; | |||||
| Array.Resize(ref data, _data.Length + 1); | |||||
| data[0] = _reportId; | |||||
| Array.Copy(_data, 0, data, 1, _data.Length); | |||||
| return data; | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,47 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace HidLibrary.Honeywell1902Series | |||||
| {public class Report1902 | |||||
| { | |||||
| private readonly byte[] _data; | |||||
| private readonly HidDeviceData.ReadStatus _status; | |||||
| public Report1902(HidReport hidReport) | |||||
| { | |||||
| _status = hidReport.ReadStatus; | |||||
| ReportId = hidReport.ReportId; | |||||
| Exists = hidReport.Exists; | |||||
| if (hidReport.Data.Length > 0) Length = hidReport.Data[0]; | |||||
| if (hidReport.Data.Length > 1) AimSymbologyId0 = hidReport.Data[1]; | |||||
| if (hidReport.Data.Length > 2) AimSymbologyId1 = hidReport.Data[2]; | |||||
| if (hidReport.Data.Length > 3) AimSymbologyId2 = hidReport.Data[3]; | |||||
| if (hidReport.Data.Length > Length + 3) | |||||
| { | |||||
| Array.Resize(ref _data, Length); | |||||
| Array.Copy(hidReport.Data, 4, _data, 0, Length); | |||||
| } | |||||
| if (hidReport.Data.Length > 60) HhpSymbologyId = hidReport.Data[60]; | |||||
| if (hidReport.Data.Length > 61) Reserved = hidReport.Data[61]; | |||||
| if (hidReport.Data.Length > 62) MoreData = hidReport.Data[62] == 1; | |||||
| } | |||||
| public HidDeviceData.ReadStatus ReadStatus { get { return _status; } } | |||||
| public byte[] Data { get { return _data; } } | |||||
| public bool Exists { get; private set; } | |||||
| public byte ReportId { get; private set; } | |||||
| public byte Length { get; private set; } | |||||
| public byte AimSymbologyId0 { get; private set; } | |||||
| public byte AimSymbologyId1 { get; private set; } | |||||
| public byte AimSymbologyId2 { get; private set; } | |||||
| public byte HhpSymbologyId { get; private set; } | |||||
| public byte Reserved { get; private set; } | |||||
| public bool MoreData { get; private set; } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,154 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading; | |||||
| using System.Threading.Tasks; | |||||
| namespace HidLibrary.Honeywell1902Series | |||||
| { | |||||
| public class Scanner1902 : IDisposable | |||||
| { | |||||
| private const int HidReportId = 0x4; | |||||
| private const int ErrorBeepMessage = 0x20; | |||||
| private const int SuccessBeepMessage = 0x40; | |||||
| private const int StartScanMessage = 0x4; | |||||
| private const int EndScanMessage = 0x1; | |||||
| public event InsertedEventHandler Inserted; | |||||
| public event RemovedEventHandler Removed; | |||||
| public event DataRecievedEventHandler DataRecieved; | |||||
| public delegate void InsertedEventHandler(); | |||||
| public delegate void RemovedEventHandler(); | |||||
| public delegate void DataRecievedEventHandler(byte[] data); | |||||
| private readonly HidDevice _scanner; | |||||
| private int _isReading; | |||||
| public Scanner1902(string devicePath) : this(HidDevices.GetDevice(devicePath)) { } | |||||
| public Scanner1902(HidDevice hidDevice) | |||||
| { | |||||
| _scanner = hidDevice; | |||||
| _scanner.Inserted += ScannerInserted; | |||||
| _scanner.Removed += ScannerRemoved; | |||||
| if (!_scanner.IsOpen) _scanner.OpenDevice(); | |||||
| _scanner.MonitorDeviceEvents = true; | |||||
| BeginReadReport(); | |||||
| } | |||||
| public string DevicePath { get { return _scanner.DevicePath; } } | |||||
| public bool IsListening { get; private set; } | |||||
| public bool IsConnected { get { return _scanner.IsConnected; } } | |||||
| public static IEnumerable<Scanner1902> Enumerate(int vid, int pid) | |||||
| { | |||||
| return HidDevices.Enumerate(vid, pid).Select(x => new Scanner1902(x)); | |||||
| } | |||||
| public void ErrorBeep() | |||||
| { | |||||
| var report = _scanner.CreateReport(); | |||||
| report.ReportId = HidReportId; | |||||
| report.Data[0] = ErrorBeepMessage; | |||||
| _scanner.WriteReport(report); | |||||
| } | |||||
| public void SuccessBeep() | |||||
| { | |||||
| var report = _scanner.CreateReport(); | |||||
| report.ReportId = HidReportId; | |||||
| report.Data[0] = SuccessBeepMessage; | |||||
| _scanner.WriteReport(report); | |||||
| } | |||||
| public void StartScan() | |||||
| { | |||||
| var report = _scanner.CreateReport(); | |||||
| report.ReportId = HidReportId; | |||||
| report.Data[0] = StartScanMessage; | |||||
| _scanner.WriteReport(report); | |||||
| } | |||||
| public void EndScan() | |||||
| { | |||||
| var report = _scanner.CreateReport(); | |||||
| report.ReportId = HidReportId; | |||||
| report.Data[0] = EndScanMessage; | |||||
| _scanner.WriteReport(report); | |||||
| } | |||||
| public void StartListen() { IsListening = true; } | |||||
| public void StopListen() { IsListening = false; } | |||||
| private void BeginReadReport() | |||||
| { | |||||
| if (Interlocked.CompareExchange(ref _isReading, 1, 0) == 1) return; | |||||
| _scanner.ReadReport(ReadReport); | |||||
| } | |||||
| private void ReadReport(HidReport report) | |||||
| { | |||||
| var scannerReport = new Report1902(report); | |||||
| var data = new byte[] { }; | |||||
| var currentPosition = 0; | |||||
| var readRequired = false; | |||||
| if (scannerReport.Length > 0 & scannerReport.ReadStatus == HidDeviceData.ReadStatus.Success) | |||||
| { | |||||
| do | |||||
| { | |||||
| if (scannerReport.MoreData && readRequired) scannerReport = new Report1902(_scanner.ReadReport()); | |||||
| else readRequired = true; | |||||
| if (!scannerReport.Exists) continue; | |||||
| Array.Resize(ref data, data.GetUpperBound(0) + scannerReport.Data.Length + 1); | |||||
| Array.Copy(scannerReport.Data, 0, data, currentPosition, scannerReport.Data.Length); | |||||
| currentPosition += scannerReport.Data.Length; | |||||
| } while (scannerReport.MoreData && scannerReport.Exists); | |||||
| if (IsListening && data.Length > 0 && DataRecieved != null) DataRecieved(data); | |||||
| } | |||||
| if (scannerReport.ReadStatus != HidDeviceData.ReadStatus.NotConnected) _scanner.ReadReport(ReadReport); | |||||
| else _isReading = 0; | |||||
| } | |||||
| private void ScannerInserted() | |||||
| { | |||||
| BeginReadReport(); | |||||
| if (Inserted != null) Inserted(); | |||||
| } | |||||
| private void ScannerRemoved() | |||||
| { | |||||
| if (Removed != null) Removed(); | |||||
| } | |||||
| public void Dispose() | |||||
| { | |||||
| _scanner.CloseDevice(); | |||||
| GC.SuppressFinalize(this); | |||||
| } | |||||
| ~Scanner1902() | |||||
| { | |||||
| Dispose(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,99 @@ | |||||
| using System; | |||||
| using System.Threading.Tasks; | |||||
| namespace HidLibrary | |||||
| { | |||||
| public delegate void InsertedEventHandler(); | |||||
| public delegate void RemovedEventHandler(); | |||||
| public enum DeviceMode | |||||
| { | |||||
| NonOverlapped = 0, | |||||
| Overlapped = 1 | |||||
| } | |||||
| [Flags] | |||||
| public enum ShareMode | |||||
| { | |||||
| Exclusive = 0, | |||||
| ShareRead = NativeMethods.FILE_SHARE_READ, | |||||
| ShareWrite = NativeMethods.FILE_SHARE_WRITE | |||||
| } | |||||
| public delegate void ReadCallback(HidDeviceData data); | |||||
| public delegate void ReadReportCallback(HidReport report); | |||||
| public delegate void WriteCallback(bool success); | |||||
| public interface IHidDevice : IDisposable | |||||
| { | |||||
| event InsertedEventHandler Inserted; | |||||
| event RemovedEventHandler Removed; | |||||
| IntPtr Handle { get; } | |||||
| bool IsOpen { get; } | |||||
| bool IsConnected { get; } | |||||
| string Description { get; } | |||||
| HidDeviceCapabilities Capabilities { get; } | |||||
| HidDeviceAttributes Attributes { get; } | |||||
| string DevicePath { get; } | |||||
| bool MonitorDeviceEvents { get; set; } | |||||
| void OpenDevice(); | |||||
| void OpenDevice(DeviceMode readMode, DeviceMode writeMode, ShareMode shareMode); | |||||
| void CloseDevice(); | |||||
| HidDeviceData Read(); | |||||
| void Read(ReadCallback callback); | |||||
| void Read(ReadCallback callback, int timeout); | |||||
| Task<HidDeviceData> ReadAsync(int timeout = 0); | |||||
| HidDeviceData Read(int timeout); | |||||
| void ReadReport(ReadReportCallback callback); | |||||
| void ReadReport(ReadReportCallback callback, int timeout); | |||||
| Task<HidReport> ReadReportAsync(int timeout = 0); | |||||
| HidReport ReadReport(int timeout); | |||||
| HidReport ReadReport(); | |||||
| bool ReadFeatureData(out byte[] data, byte reportId = 0); | |||||
| bool ReadProduct(out byte[] data); | |||||
| bool ReadManufacturer(out byte[] data); | |||||
| bool ReadSerialNumber(out byte[] data); | |||||
| void Write(byte[] data, WriteCallback callback); | |||||
| bool Write(byte[] data); | |||||
| bool Write(byte[] data, int timeout); | |||||
| void Write(byte[] data, WriteCallback callback, int timeout); | |||||
| Task<bool> WriteAsync(byte[] data, int timeout = 0); | |||||
| void WriteReport(HidReport report, WriteCallback callback); | |||||
| bool WriteReport(HidReport report); | |||||
| bool WriteReport(HidReport report, int timeout); | |||||
| void WriteReport(HidReport report, WriteCallback callback, int timeout); | |||||
| Task<bool> WriteReportAsync(HidReport report, int timeout = 0); | |||||
| HidReport CreateReport(); | |||||
| bool WriteFeatureData(byte[] data); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,57 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| namespace HidLibrary | |||||
| { | |||||
| public interface IHidEnumerator | |||||
| { | |||||
| bool IsConnected(string devicePath); | |||||
| IHidDevice GetDevice(string devicePath); | |||||
| IEnumerable<IHidDevice> Enumerate(); | |||||
| IEnumerable<IHidDevice> Enumerate(string devicePath); | |||||
| IEnumerable<IHidDevice> Enumerate(int vendorId, params int[] productIds); | |||||
| IEnumerable<IHidDevice> Enumerate(int vendorId); | |||||
| } | |||||
| // Instance class that wraps HidDevices | |||||
| // The purpose of this is to allow consumer classes to create | |||||
| // their own enumeration abstractions, either for testing or | |||||
| // for comparing different implementations | |||||
| public class HidEnumerator : IHidEnumerator | |||||
| { | |||||
| public bool IsConnected(string devicePath) | |||||
| { | |||||
| return HidDevices.IsConnected(devicePath); | |||||
| } | |||||
| public IHidDevice GetDevice(string devicePath) | |||||
| { | |||||
| return HidDevices.GetDevice(devicePath) as IHidDevice; | |||||
| } | |||||
| public IEnumerable<IHidDevice> Enumerate() | |||||
| { | |||||
| return HidDevices.Enumerate(). | |||||
| Select(d => d as IHidDevice); | |||||
| } | |||||
| public IEnumerable<IHidDevice> Enumerate(string devicePath) | |||||
| { | |||||
| return HidDevices.Enumerate(devicePath). | |||||
| Select(d => d as IHidDevice); | |||||
| } | |||||
| public IEnumerable<IHidDevice> Enumerate(int vendorId, params int[] productIds) | |||||
| { | |||||
| return HidDevices.Enumerate(vendorId, productIds). | |||||
| Select(d => d as IHidDevice); | |||||
| } | |||||
| public IEnumerable<IHidDevice> Enumerate(int vendorId) | |||||
| { | |||||
| return HidDevices.Enumerate(vendorId). | |||||
| Select(d => d as IHidDevice); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,344 @@ | |||||
| using System; | |||||
| using System.Runtime.InteropServices; | |||||
| namespace HidLibrary | |||||
| { | |||||
| internal static class NativeMethods | |||||
| { | |||||
| internal const int FILE_FLAG_OVERLAPPED = 0x40000000; | |||||
| internal const short FILE_SHARE_READ = 0x1; | |||||
| internal const short FILE_SHARE_WRITE = 0x2; | |||||
| internal const uint GENERIC_READ = 0x80000000; | |||||
| internal const uint GENERIC_WRITE = 0x40000000; | |||||
| internal const int ACCESS_NONE = 0; | |||||
| internal const int INVALID_HANDLE_VALUE = -1; | |||||
| internal const short OPEN_EXISTING = 3; | |||||
| internal const int WAIT_TIMEOUT = 0x102; | |||||
| internal const uint WAIT_OBJECT_0 = 0; | |||||
| internal const uint WAIT_FAILED = 0xffffffff; | |||||
| internal const int WAIT_INFINITE = -1; | |||||
| [StructLayout(LayoutKind.Sequential)] | |||||
| internal struct OVERLAPPED | |||||
| { | |||||
| public int Internal; | |||||
| public int InternalHigh; | |||||
| public int Offset; | |||||
| public int OffsetHigh; | |||||
| public int hEvent; | |||||
| } | |||||
| [StructLayout(LayoutKind.Sequential)] | |||||
| internal struct SECURITY_ATTRIBUTES | |||||
| { | |||||
| public int nLength; | |||||
| public IntPtr lpSecurityDescriptor; | |||||
| public bool bInheritHandle; | |||||
| } | |||||
| [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] | |||||
| static internal extern bool CancelIo(IntPtr hFile); | |||||
| [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] | |||||
| static internal extern bool CancelIoEx(IntPtr hFile, IntPtr lpOverlapped); | |||||
| [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] | |||||
| static internal extern bool CloseHandle(IntPtr hObject); | |||||
| [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] | |||||
| static internal extern bool CancelSynchronousIo(IntPtr hObject); | |||||
| [DllImport("kernel32.dll", CharSet = CharSet.Auto)] | |||||
| static internal extern IntPtr CreateEvent(ref SECURITY_ATTRIBUTES securityAttributes, int bManualReset, int bInitialState, string lpName); | |||||
| [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] | |||||
| static internal extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, ref SECURITY_ATTRIBUTES lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile); | |||||
| [DllImport("kernel32.dll", SetLastError = true)] | |||||
| static internal extern bool ReadFile(IntPtr hFile, IntPtr lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, [In] ref System.Threading.NativeOverlapped lpOverlapped); | |||||
| [DllImport("kernel32.dll")] | |||||
| static internal extern uint WaitForSingleObject(IntPtr hHandle, int dwMilliseconds); | |||||
| [DllImport("kernel32.dll", SetLastError = true)] | |||||
| static internal extern bool GetOverlappedResult(IntPtr hFile, [In] ref System.Threading.NativeOverlapped lpOverlapped, out uint lpNumberOfBytesTransferred, bool bWait); | |||||
| [DllImport("kernel32.dll")] | |||||
| static internal extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, [In] ref System.Threading.NativeOverlapped lpOverlapped); | |||||
| internal const int DBT_DEVICEARRIVAL = 0x8000; | |||||
| internal const int DBT_DEVICEREMOVECOMPLETE = 0x8004; | |||||
| internal const int DBT_DEVTYP_DEVICEINTERFACE = 5; | |||||
| internal const int DBT_DEVTYP_HANDLE = 6; | |||||
| internal const int DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 4; | |||||
| internal const int DEVICE_NOTIFY_SERVICE_HANDLE = 1; | |||||
| internal const int DEVICE_NOTIFY_WINDOW_HANDLE = 0; | |||||
| internal const int WM_DEVICECHANGE = 0x219; | |||||
| internal const short DIGCF_PRESENT = 0x2; | |||||
| internal const short DIGCF_DEVICEINTERFACE = 0x10; | |||||
| internal const int DIGCF_ALLCLASSES = 0x4; | |||||
| internal const int MAX_DEV_LEN = 1000; | |||||
| internal const int SPDRP_ADDRESS = 0x1c; | |||||
| internal const int SPDRP_BUSNUMBER = 0x15; | |||||
| internal const int SPDRP_BUSTYPEGUID = 0x13; | |||||
| internal const int SPDRP_CAPABILITIES = 0xf; | |||||
| internal const int SPDRP_CHARACTERISTICS = 0x1b; | |||||
| internal const int SPDRP_CLASS = 7; | |||||
| internal const int SPDRP_CLASSGUID = 8; | |||||
| internal const int SPDRP_COMPATIBLEIDS = 2; | |||||
| internal const int SPDRP_CONFIGFLAGS = 0xa; | |||||
| internal const int SPDRP_DEVICE_POWER_DATA = 0x1e; | |||||
| internal const int SPDRP_DEVICEDESC = 0; | |||||
| internal const int SPDRP_DEVTYPE = 0x19; | |||||
| internal const int SPDRP_DRIVER = 9; | |||||
| internal const int SPDRP_ENUMERATOR_NAME = 0x16; | |||||
| internal const int SPDRP_EXCLUSIVE = 0x1a; | |||||
| internal const int SPDRP_FRIENDLYNAME = 0xc; | |||||
| internal const int SPDRP_HARDWAREID = 1; | |||||
| internal const int SPDRP_LEGACYBUSTYPE = 0x14; | |||||
| internal const int SPDRP_LOCATION_INFORMATION = 0xd; | |||||
| internal const int SPDRP_LOWERFILTERS = 0x12; | |||||
| internal const int SPDRP_MFG = 0xb; | |||||
| internal const int SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0xe; | |||||
| internal const int SPDRP_REMOVAL_POLICY = 0x1f; | |||||
| internal const int SPDRP_REMOVAL_POLICY_HW_DEFAULT = 0x20; | |||||
| internal const int SPDRP_REMOVAL_POLICY_OVERRIDE = 0x21; | |||||
| internal const int SPDRP_SECURITY = 0x17; | |||||
| internal const int SPDRP_SECURITY_SDS = 0x18; | |||||
| internal const int SPDRP_SERVICE = 4; | |||||
| internal const int SPDRP_UI_NUMBER = 0x10; | |||||
| internal const int SPDRP_UI_NUMBER_DESC_FORMAT = 0x1d; | |||||
| internal const int SPDRP_UPPERFILTERS = 0x11; | |||||
| [StructLayout(LayoutKind.Sequential)] | |||||
| internal class DEV_BROADCAST_DEVICEINTERFACE | |||||
| { | |||||
| internal int dbcc_size; | |||||
| internal int dbcc_devicetype; | |||||
| internal int dbcc_reserved; | |||||
| internal Guid dbcc_classguid; | |||||
| internal short dbcc_name; | |||||
| } | |||||
| [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | |||||
| internal class DEV_BROADCAST_DEVICEINTERFACE_1 | |||||
| { | |||||
| internal int dbcc_size; | |||||
| internal int dbcc_devicetype; | |||||
| internal int dbcc_reserved; | |||||
| [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 16)] | |||||
| internal byte[] dbcc_classguid; | |||||
| [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] | |||||
| internal char[] dbcc_name; | |||||
| } | |||||
| [StructLayout(LayoutKind.Sequential)] | |||||
| internal class DEV_BROADCAST_HANDLE | |||||
| { | |||||
| internal int dbch_size; | |||||
| internal int dbch_devicetype; | |||||
| internal int dbch_reserved; | |||||
| internal int dbch_handle; | |||||
| internal int dbch_hdevnotify; | |||||
| } | |||||
| [StructLayout(LayoutKind.Sequential)] | |||||
| internal class DEV_BROADCAST_HDR | |||||
| { | |||||
| internal int dbch_size; | |||||
| internal int dbch_devicetype; | |||||
| internal int dbch_reserved; | |||||
| } | |||||
| [StructLayout(LayoutKind.Sequential)] | |||||
| internal struct SP_DEVICE_INTERFACE_DATA | |||||
| { | |||||
| internal int cbSize; | |||||
| internal System.Guid InterfaceClassGuid; | |||||
| internal int Flags; | |||||
| internal IntPtr Reserved; | |||||
| } | |||||
| [StructLayout(LayoutKind.Sequential)] | |||||
| internal struct SP_DEVINFO_DATA | |||||
| { | |||||
| internal int cbSize; | |||||
| internal Guid ClassGuid; | |||||
| internal int DevInst; | |||||
| internal IntPtr Reserved; | |||||
| } | |||||
| [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] | |||||
| internal struct SP_DEVICE_INTERFACE_DETAIL_DATA | |||||
| { | |||||
| internal int Size; | |||||
| [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] | |||||
| internal string DevicePath; | |||||
| } | |||||
| [StructLayout(LayoutKind.Sequential)] | |||||
| internal struct DEVPROPKEY | |||||
| { | |||||
| public Guid fmtid; | |||||
| public ulong pid; | |||||
| } | |||||
| internal static DEVPROPKEY DEVPKEY_Device_BusReportedDeviceDesc = | |||||
| new DEVPROPKEY { fmtid = new Guid(0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2), pid = 4 }; | |||||
| [DllImport("setupapi.dll", EntryPoint = "SetupDiGetDeviceRegistryProperty")] | |||||
| public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, int propertyVal, ref int propertyRegDataType, byte[] propertyBuffer, int propertyBufferSize, ref int requiredSize); | |||||
| [DllImport("setupapi.dll", EntryPoint = "SetupDiGetDevicePropertyW", SetLastError = true)] | |||||
| public static extern bool SetupDiGetDeviceProperty(IntPtr deviceInfo, ref SP_DEVINFO_DATA deviceInfoData, ref DEVPROPKEY propkey, ref ulong propertyDataType, byte[] propertyBuffer, int propertyBufferSize, ref int requiredSize, uint flags); | |||||
| [DllImport("setupapi.dll")] | |||||
| static internal extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet, int memberIndex, ref SP_DEVINFO_DATA deviceInfoData); | |||||
| [DllImport("user32.dll", CharSet = CharSet.Auto)] | |||||
| static internal extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr notificationFilter, Int32 flags); | |||||
| [DllImport("setupapi.dll")] | |||||
| internal static extern int SetupDiCreateDeviceInfoList(ref Guid classGuid, int hwndParent); | |||||
| [DllImport("setupapi.dll")] | |||||
| static internal extern int SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet); | |||||
| [DllImport("setupapi.dll")] | |||||
| static internal extern bool SetupDiEnumDeviceInterfaces(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, ref Guid interfaceClassGuid, int memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData); | |||||
| [DllImport("setupapi.dll", CharSet = CharSet.Auto)] | |||||
| static internal extern IntPtr SetupDiGetClassDevs(ref System.Guid classGuid, string enumerator, int hwndParent, int flags); | |||||
| [DllImport("setupapi.dll", CharSet = CharSet.Auto, EntryPoint = "SetupDiGetDeviceInterfaceDetail")] | |||||
| static internal extern bool SetupDiGetDeviceInterfaceDetailBuffer(IntPtr deviceInfoSet, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr deviceInterfaceDetailData, int deviceInterfaceDetailDataSize, ref int requiredSize, IntPtr deviceInfoData); | |||||
| [DllImport("setupapi.dll", CharSet = CharSet.Auto)] | |||||
| static internal extern bool SetupDiGetDeviceInterfaceDetail(IntPtr deviceInfoSet, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, int deviceInterfaceDetailDataSize, ref int requiredSize, IntPtr deviceInfoData); | |||||
| [DllImport("user32.dll")] | |||||
| static internal extern bool UnregisterDeviceNotification(IntPtr handle); | |||||
| internal const short HIDP_INPUT = 0; | |||||
| internal const short HIDP_OUTPUT = 1; | |||||
| internal const short HIDP_FEATURE = 2; | |||||
| [StructLayout(LayoutKind.Sequential)] | |||||
| internal struct HIDD_ATTRIBUTES | |||||
| { | |||||
| internal int Size; | |||||
| internal ushort VendorID; | |||||
| internal ushort ProductID; | |||||
| internal short VersionNumber; | |||||
| } | |||||
| [StructLayout(LayoutKind.Sequential)] | |||||
| internal struct HIDP_CAPS | |||||
| { | |||||
| internal short Usage; | |||||
| internal short UsagePage; | |||||
| internal short InputReportByteLength; | |||||
| internal short OutputReportByteLength; | |||||
| internal short FeatureReportByteLength; | |||||
| [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] | |||||
| internal short[] Reserved; | |||||
| internal short NumberLinkCollectionNodes; | |||||
| internal short NumberInputButtonCaps; | |||||
| internal short NumberInputValueCaps; | |||||
| internal short NumberInputDataIndices; | |||||
| internal short NumberOutputButtonCaps; | |||||
| internal short NumberOutputValueCaps; | |||||
| internal short NumberOutputDataIndices; | |||||
| internal short NumberFeatureButtonCaps; | |||||
| internal short NumberFeatureValueCaps; | |||||
| internal short NumberFeatureDataIndices; | |||||
| } | |||||
| [StructLayout(LayoutKind.Sequential)] | |||||
| internal struct HIDP_VALUE_CAPS | |||||
| { | |||||
| internal short UsagePage; | |||||
| internal byte ReportID; | |||||
| internal int IsAlias; | |||||
| internal short BitField; | |||||
| internal short LinkCollection; | |||||
| internal short LinkUsage; | |||||
| internal short LinkUsagePage; | |||||
| internal int IsRange; | |||||
| internal int IsStringRange; | |||||
| internal int IsDesignatorRange; | |||||
| internal int IsAbsolute; | |||||
| internal int HasNull; | |||||
| internal byte Reserved; | |||||
| internal short BitSize; | |||||
| internal short ReportCount; | |||||
| internal short Reserved2; | |||||
| internal short Reserved3; | |||||
| internal short Reserved4; | |||||
| internal short Reserved5; | |||||
| internal short Reserved6; | |||||
| internal int LogicalMin; | |||||
| internal int LogicalMax; | |||||
| internal int PhysicalMin; | |||||
| internal int PhysicalMax; | |||||
| internal short UsageMin; | |||||
| internal short UsageMax; | |||||
| internal short StringMin; | |||||
| internal short StringMax; | |||||
| internal short DesignatorMin; | |||||
| internal short DesignatorMax; | |||||
| internal short DataIndexMin; | |||||
| internal short DataIndexMax; | |||||
| } | |||||
| [DllImport("hid.dll")] | |||||
| static internal extern bool HidD_FlushQueue(IntPtr hidDeviceObject); | |||||
| [DllImport("hid.dll")] | |||||
| static internal extern bool HidD_GetAttributes(IntPtr hidDeviceObject, ref HIDD_ATTRIBUTES attributes); | |||||
| [DllImport("hid.dll")] | |||||
| static internal extern bool HidD_GetFeature(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength); | |||||
| [DllImport("hid.dll")] | |||||
| static internal extern bool HidD_GetInputReport(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength); | |||||
| [DllImport("hid.dll")] | |||||
| static internal extern void HidD_GetHidGuid(ref Guid hidGuid); | |||||
| [DllImport("hid.dll")] | |||||
| static internal extern bool HidD_GetNumInputBuffers(IntPtr hidDeviceObject, ref int numberBuffers); | |||||
| [DllImport("hid.dll")] | |||||
| static internal extern bool HidD_GetPreparsedData(IntPtr hidDeviceObject, ref IntPtr preparsedData); | |||||
| [DllImport("hid.dll")] | |||||
| static internal extern bool HidD_FreePreparsedData(IntPtr preparsedData); | |||||
| [DllImport("hid.dll")] | |||||
| static internal extern bool HidD_SetFeature(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength); | |||||
| [DllImport("hid.dll")] | |||||
| static internal extern bool HidD_SetNumInputBuffers(IntPtr hidDeviceObject, int numberBuffers); | |||||
| [DllImport("hid.dll")] | |||||
| static internal extern bool HidD_SetOutputReport(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength); | |||||
| [DllImport("hid.dll")] | |||||
| static internal extern int HidP_GetCaps(IntPtr preparsedData, ref HIDP_CAPS capabilities); | |||||
| [DllImport("hid.dll")] | |||||
| static internal extern int HidP_GetValueCaps(short reportType, ref byte valueCaps, ref short valueCapsLength, IntPtr preparsedData); | |||||
| [DllImport("hid.dll", CharSet = CharSet.Unicode)] | |||||
| internal static extern bool HidD_GetProductString(IntPtr hidDeviceObject, ref byte lpReportBuffer, int ReportBufferLength); | |||||
| [DllImport("hid.dll", CharSet = CharSet.Unicode)] | |||||
| internal static extern bool HidD_GetManufacturerString(IntPtr hidDeviceObject, ref byte lpReportBuffer, int ReportBufferLength); | |||||
| [DllImport("hid.dll", CharSet = CharSet.Unicode)] | |||||
| internal static extern bool HidD_GetSerialNumberString(IntPtr hidDeviceObject, ref byte lpReportBuffer, int reportBufferLength); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,36 @@ | |||||
| using System.Reflection; | |||||
| using System.Runtime.CompilerServices; | |||||
| using System.Runtime.InteropServices; | |||||
| // 有关程序集的常规信息通过以下 | |||||
| // 特性集控制。更改这些特性值可修改 | |||||
| // 与程序集关联的信息。 | |||||
| [assembly: AssemblyTitle("HidLibrary")] | |||||
| [assembly: AssemblyDescription("")] | |||||
| [assembly: AssemblyConfiguration("")] | |||||
| [assembly: AssemblyCompany("")] | |||||
| [assembly: AssemblyProduct("HidLibrary")] | |||||
| [assembly: AssemblyCopyright("Copyright © 2018")] | |||||
| [assembly: AssemblyTrademark("")] | |||||
| [assembly: AssemblyCulture("")] | |||||
| // 将 ComVisible 设置为 false 使此程序集中的类型 | |||||
| // 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, | |||||
| // 则将该类型上的 ComVisible 特性设置为 true。 | |||||
| [assembly: ComVisible(false)] | |||||
| // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID | |||||
| [assembly: Guid("72aa9d31-9202-4f22-907e-3894735c9f06")] | |||||
| // 程序集的版本信息由下面四个值组成: | |||||
| // | |||||
| // 主版本 | |||||
| // 次版本 | |||||
| // 生成号 | |||||
| // 修订号 | |||||
| // | |||||
| // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, | |||||
| // 方法是按如下所示使用“*”: | |||||
| // [assembly: AssemblyVersion("1.0.*")] | |||||
| [assembly: AssemblyVersion("1.0.0.0")] | |||||
| [assembly: AssemblyFileVersion("1.0.0.0")] | |||||