From 2b553e16ad3d6e74620f8d2ad3e7f969b8ba6145 Mon Sep 17 00:00:00 2001 From: yibo <361071264@qq.com> Date: Fri, 19 Oct 2018 14:13:40 +0800 Subject: [PATCH] . --- HidLibrary/Extensions.cs | 19 + HidLibrary/HidAsyncState.cs | 17 + HidLibrary/HidDevice.cs | 711 ++++++++++++++++++ HidLibrary/HidDeviceAttributes.cs | 21 + HidLibrary/HidDeviceCapabilities.cs | 43 ++ HidLibrary/HidDeviceData.cs | 30 + HidLibrary/HidDeviceEventMonitor.cs | 49 ++ HidLibrary/HidDevices.cs | 157 ++++ HidLibrary/HidFastReadDevice.cs | 76 ++ HidLibrary/HidFastReadEnumerator.cs | 44 ++ HidLibrary/HidLibrary.csproj | 71 ++ HidLibrary/HidReport.cs | 75 ++ HidLibrary/Honeywell1902Series/Report1902.cs | 47 ++ HidLibrary/Honeywell1902Series/Scanner1902.cs | 154 ++++ HidLibrary/IHidDevice.cs | 99 +++ HidLibrary/IHidEnumerator.cs | 57 ++ HidLibrary/NativeMethods.cs | 344 +++++++++ HidLibrary/Properties/AssemblyInfo.cs | 36 + WinFormControl.sln | 12 + WinFormControl/UScanPanel.cs | 3 +- 20 files changed, 2064 insertions(+), 1 deletion(-) create mode 100644 HidLibrary/Extensions.cs create mode 100644 HidLibrary/HidAsyncState.cs create mode 100644 HidLibrary/HidDevice.cs create mode 100644 HidLibrary/HidDeviceAttributes.cs create mode 100644 HidLibrary/HidDeviceCapabilities.cs create mode 100644 HidLibrary/HidDeviceData.cs create mode 100644 HidLibrary/HidDeviceEventMonitor.cs create mode 100644 HidLibrary/HidDevices.cs create mode 100644 HidLibrary/HidFastReadDevice.cs create mode 100644 HidLibrary/HidFastReadEnumerator.cs create mode 100644 HidLibrary/HidLibrary.csproj create mode 100644 HidLibrary/HidReport.cs create mode 100644 HidLibrary/Honeywell1902Series/Report1902.cs create mode 100644 HidLibrary/Honeywell1902Series/Scanner1902.cs create mode 100644 HidLibrary/IHidDevice.cs create mode 100644 HidLibrary/IHidEnumerator.cs create mode 100644 HidLibrary/NativeMethods.cs create mode 100644 HidLibrary/Properties/AssemblyInfo.cs diff --git a/HidLibrary/Extensions.cs b/HidLibrary/Extensions.cs new file mode 100644 index 0000000..aa374fb --- /dev/null +++ b/HidLibrary/Extensions.cs @@ -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)); + } + } +} \ No newline at end of file diff --git a/HidLibrary/HidAsyncState.cs b/HidLibrary/HidAsyncState.cs new file mode 100644 index 0000000..b0e7b25 --- /dev/null +++ b/HidLibrary/HidAsyncState.cs @@ -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; } } + } +} diff --git a/HidLibrary/HidDevice.cs b/HidLibrary/HidDevice.cs new file mode 100644 index 0000000..b80bd75 --- /dev/null +++ b/HidLibrary/HidDevice.cs @@ -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 ReadAsync(int timeout = 0) + { + var readDelegate = new ReadDelegate(Read); + return await Task.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 ReadReportAsync(int timeout = 0) + { + var readReportDelegate = new ReadReportDelegate(ReadReport); + return await Task.Factory.FromAsync(readReportDelegate.BeginInvoke, readReportDelegate.EndInvoke, timeout, null); + } + + /// + /// 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. + /// + /// The report ID to read from the device + /// The HID report that is read. The report will contain the success status of the read request + /// + 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 WriteAsync(byte[] data, int timeout = 0) + { + var writeDelegate = new WriteDelegate(Write); + return await Task.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); + } + + /// + /// Handle data transfers on the control channel. This method places data on the control channel for devices + /// that do not support the interupt transfers + /// + /// The outbound HID report + /// The result of the tranfer request: true if successful otherwise false + /// + 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 WriteReportAsync(HidReport report, int timeout = 0) + { + var writeReportDelegate = new WriteReportDelegate(WriteReport); + return await Task.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(); + } + } +} diff --git a/HidLibrary/HidDeviceAttributes.cs b/HidLibrary/HidDeviceAttributes.cs new file mode 100644 index 0000000..af2d173 --- /dev/null +++ b/HidLibrary/HidDeviceAttributes.cs @@ -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; } + } +} diff --git a/HidLibrary/HidDeviceCapabilities.cs b/HidLibrary/HidDeviceCapabilities.cs new file mode 100644 index 0000000..c4f89b8 --- /dev/null +++ b/HidLibrary/HidDeviceCapabilities.cs @@ -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; } + } +} diff --git a/HidLibrary/HidDeviceData.cs b/HidLibrary/HidDeviceData.cs new file mode 100644 index 0000000..5830fbe --- /dev/null +++ b/HidLibrary/HidDeviceData.cs @@ -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; } + } +} diff --git a/HidLibrary/HidDeviceEventMonitor.cs b/HidLibrary/HidDeviceEventMonitor.cs new file mode 100644 index 0000000..81842ce --- /dev/null +++ b/HidLibrary/HidDeviceEventMonitor.cs @@ -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); + } + } +} diff --git a/HidLibrary/HidDevices.cs b/HidLibrary/HidDevices.cs new file mode 100644 index 0000000..1dc96cd --- /dev/null +++ b/HidLibrary/HidDevices.cs @@ -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 Enumerate() + { + return EnumerateDevices().Select(x => new HidDevice(x.Path, x.Description)); + } + + public static IEnumerable Enumerate(string devicePath) + { + return EnumerateDevices().Where(x => x.Path == devicePath).Select(x => new HidDevice(x.Path, x.Description)); + } + + public static IEnumerable 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 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 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 EnumerateDevices() + { + var devices = new List(); + 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; + } + } +} diff --git a/HidLibrary/HidFastReadDevice.cs b/HidLibrary/HidFastReadDevice.cs new file mode 100644 index 0000000..154aef0 --- /dev/null +++ b/HidLibrary/HidFastReadDevice.cs @@ -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 FastReadAsync(int timeout = 0) + { + var readDelegate = new ReadDelegate(FastRead); + return await Task.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 FastReadReportAsync(int timeout = 0) + { + var readReportDelegate = new ReadReportDelegate(FastReadReport); + return await Task.Factory.FromAsync(readReportDelegate.BeginInvoke, readReportDelegate.EndInvoke, timeout, null); + } + } +} diff --git a/HidLibrary/HidFastReadEnumerator.cs b/HidLibrary/HidFastReadEnumerator.cs new file mode 100644 index 0000000..acbe7dc --- /dev/null +++ b/HidLibrary/HidFastReadEnumerator.cs @@ -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 Enumerate() + { + return HidDevices.EnumerateDevices(). + Select(d => new HidFastReadDevice(d.Path, d.Description) as IHidDevice); + } + + public IEnumerable Enumerate(string devicePath) + { + return HidDevices.EnumerateDevices().Where(x => x.Path == devicePath). + Select(d => new HidFastReadDevice(d.Path, d.Description) as IHidDevice); + } + + public IEnumerable 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 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); + } + } +} \ No newline at end of file diff --git a/HidLibrary/HidLibrary.csproj b/HidLibrary/HidLibrary.csproj new file mode 100644 index 0000000..3d7061d --- /dev/null +++ b/HidLibrary/HidLibrary.csproj @@ -0,0 +1,71 @@ + + + + + Debug + AnyCPU + {41D547F1-D57D-4D50-9602-6EE65CA958B2} + Library + Properties + HidLibrary + HidLibrary + v4.5 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/HidLibrary/HidReport.cs b/HidLibrary/HidReport.cs new file mode 100644 index 0000000..c550fd3 --- /dev/null +++ b/HidLibrary/HidReport.cs @@ -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; + } + } +} diff --git a/HidLibrary/Honeywell1902Series/Report1902.cs b/HidLibrary/Honeywell1902Series/Report1902.cs new file mode 100644 index 0000000..b6262d8 --- /dev/null +++ b/HidLibrary/Honeywell1902Series/Report1902.cs @@ -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; } + } +} diff --git a/HidLibrary/Honeywell1902Series/Scanner1902.cs b/HidLibrary/Honeywell1902Series/Scanner1902.cs new file mode 100644 index 0000000..cd5f408 --- /dev/null +++ b/HidLibrary/Honeywell1902Series/Scanner1902.cs @@ -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 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(); + } + } +} diff --git a/HidLibrary/IHidDevice.cs b/HidLibrary/IHidDevice.cs new file mode 100644 index 0000000..d352c28 --- /dev/null +++ b/HidLibrary/IHidDevice.cs @@ -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 ReadAsync(int timeout = 0); + + HidDeviceData Read(int timeout); + + void ReadReport(ReadReportCallback callback); + + void ReadReport(ReadReportCallback callback, int timeout); + + Task 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 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 WriteReportAsync(HidReport report, int timeout = 0); + + HidReport CreateReport(); + + bool WriteFeatureData(byte[] data); + } +} diff --git a/HidLibrary/IHidEnumerator.cs b/HidLibrary/IHidEnumerator.cs new file mode 100644 index 0000000..496a364 --- /dev/null +++ b/HidLibrary/IHidEnumerator.cs @@ -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 Enumerate(); + IEnumerable Enumerate(string devicePath); + IEnumerable Enumerate(int vendorId, params int[] productIds); + IEnumerable 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 Enumerate() + { + return HidDevices.Enumerate(). + Select(d => d as IHidDevice); + } + + public IEnumerable Enumerate(string devicePath) + { + return HidDevices.Enumerate(devicePath). + Select(d => d as IHidDevice); + } + + public IEnumerable Enumerate(int vendorId, params int[] productIds) + { + return HidDevices.Enumerate(vendorId, productIds). + Select(d => d as IHidDevice); + } + + public IEnumerable Enumerate(int vendorId) + { + return HidDevices.Enumerate(vendorId). + Select(d => d as IHidDevice); + } + } +} diff --git a/HidLibrary/NativeMethods.cs b/HidLibrary/NativeMethods.cs new file mode 100644 index 0000000..43035cf --- /dev/null +++ b/HidLibrary/NativeMethods.cs @@ -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); + } +} diff --git a/HidLibrary/Properties/AssemblyInfo.cs b/HidLibrary/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5fe2b39 --- /dev/null +++ b/HidLibrary/Properties/AssemblyInfo.cs @@ -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")] diff --git a/WinFormControl.sln b/WinFormControl.sln index 57a6593..b60ec36 100644 --- a/WinFormControl.sln +++ b/WinFormControl.sln @@ -7,6 +7,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinFormControl", "WinFormControl\WinFormControl.csproj", "{5F1AC749-68DB-43E2-8895-886ECC04D7DA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfTest", "WpfTest\WpfTest.csproj", "{C9032639-3618-4F11-B6A5-54B28A5AAAC4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HidLibrary", "HidLibrary\HidLibrary.csproj", "{41D547F1-D57D-4D50-9602-6EE65CA958B2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +25,14 @@ Global {5F1AC749-68DB-43E2-8895-886ECC04D7DA}.Debug|Any CPU.Build.0 = Debug|Any CPU {5F1AC749-68DB-43E2-8895-886ECC04D7DA}.Release|Any CPU.ActiveCfg = Release|Any CPU {5F1AC749-68DB-43E2-8895-886ECC04D7DA}.Release|Any CPU.Build.0 = Release|Any CPU + {C9032639-3618-4F11-B6A5-54B28A5AAAC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C9032639-3618-4F11-B6A5-54B28A5AAAC4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C9032639-3618-4F11-B6A5-54B28A5AAAC4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C9032639-3618-4F11-B6A5-54B28A5AAAC4}.Release|Any CPU.Build.0 = Release|Any CPU + {41D547F1-D57D-4D50-9602-6EE65CA958B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41D547F1-D57D-4D50-9602-6EE65CA958B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41D547F1-D57D-4D50-9602-6EE65CA958B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41D547F1-D57D-4D50-9602-6EE65CA958B2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/WinFormControl/UScanPanel.cs b/WinFormControl/UScanPanel.cs index fbc20d2..402d587 100644 --- a/WinFormControl/UScanPanel.cs +++ b/WinFormControl/UScanPanel.cs @@ -70,7 +70,8 @@ namespace WinFormControl } else { - reading = true; + if (!reading) + reading = true; e.Handled = true; _scarText += e.KeyChar.ToString(); }