diff --git a/HIDInterface.sln b/HIDInterface.sln
index 6bc85e7..2eeb6c8 100644
--- a/HIDInterface.sln
+++ b/HIDInterface.sln
@@ -1,11 +1,11 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
-VisualStudioVersion = 12.0.30501.0
+# Visual Studio 15
+VisualStudioVersion = 15.0.27130.2003
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "USBInterface", "USBInterface\USBInterface.csproj", "{30432D4A-0128-48E7-ADCD-249D70323611}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "USBInterface", "USBInterface\USBInterface.csproj", "{30432D4A-0128-48E7-ADCD-249D70323611}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "USBInterfaceTest", "TestUSBInterface\USBInterfaceTest.csproj", "{E69C9EE3-BCA8-4E5D-8EFA-7EBD75B1087A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUSBInterface", "TestUSBInterface\TestUSBInterface.csproj", "{E69C9EE3-BCA8-4E5D-8EFA-7EBD75B1087A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -25,4 +25,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {FBCD496A-267D-40A5-8C67-F452E0F47DBD}
+ EndGlobalSection
EndGlobal
diff --git a/TestUSBInterface/Program.cs b/TestUSBInterface/Program.cs
index 6e8ec42..e2c0ef5 100644
--- a/TestUSBInterface/Program.cs
+++ b/TestUSBInterface/Program.cs
@@ -1,65 +1,31 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using USBInterface;
namespace TestUSBInterface
{
class Program
{
-
- public static void handle(object s, USBInterface.ReportEventArgs a)
+ public static void Handle(object s, ReportEventArgs a)
{
Console.WriteLine(string.Join(", ", a.Data));
}
- public static void enter(object s, EventArgs a)
+ public static void Enter(object s, DeviceScanner.DeviceArrivedArgs deviceArrivedArgs)
{
- Console.WriteLine("device arrived");
+ Console.WriteLine($"Device arrived - {deviceArrivedArgs.Path}");
}
- public static void exit(object s, EventArgs a)
+ public static void Exit(object s, DeviceScanner.DeviceRemovedArgs deviceRemovedArgs)
{
- Console.WriteLine("device removed");
+ Console.WriteLine($"Device removed - {deviceRemovedArgs.Path}");
}
static void Main(string[] args)
{
- // setup a scanner before hand
- DeviceScanner scanner = new DeviceScanner(0x4d8, 0x3f);
- scanner.DeviceArrived += enter;
- scanner.DeviceRemoved += exit;
+ var scanner = new DeviceScanner(0x20A0, 0x4241);
+ scanner.DeviceArrived += Enter;
+ scanner.DeviceRemoved += Exit;
scanner.StartAsyncScan();
- Console.WriteLine("asd");
-
- // this should probably happen in enter() function
- try
- {
- // this can all happen inside a using(...) statement
- USBDevice dev = new USBDevice(0x4d8, 0x3f, null, false, 31);
-
- Console.WriteLine(dev.Description());
-
- // add handle for data read
- dev.InputReportArrivedEvent += handle;
- // after adding the handle start reading
- dev.StartAsyncRead();
- // can add more handles at any time
- dev.InputReportArrivedEvent += handle;
-
- // write some data
- byte[] data = new byte[32];
- data[0] = 0x00;
- data[1] = 0x23;
- dev.Write(data);
-
- dev.Dispose();
- }
- catch (Exception e)
- {
- Console.WriteLine(e.Message);
- }
+ Console.WriteLine("Scanning");
Console.ReadKey();
}
}
diff --git a/TestUSBInterface/Properties/AssemblyInfo.cs b/TestUSBInterface/Properties/AssemblyInfo.cs
deleted file mode 100644
index 2ccbebd..0000000
--- a/TestUSBInterface/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("TestUSBInterface")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("TestUSBInterface")]
-[assembly: AssemblyCopyright("Copyright © 2016")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("e69c9ee3-bca8-4e5d-8efa-7ebd75b1087a")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/TestUSBInterface/TestUSBInterface.csproj b/TestUSBInterface/TestUSBInterface.csproj
new file mode 100644
index 0000000..bd0d165
--- /dev/null
+++ b/TestUSBInterface/TestUSBInterface.csproj
@@ -0,0 +1,11 @@
+
+
+ netcoreapp2.0
+ Exe
+ x86
+ $(MSBuildProgramFiles32)\dotnet\dotnet
+
+
+
+
+
\ No newline at end of file
diff --git a/TestUSBInterface/USBInterfaceTest.csproj b/TestUSBInterface/USBInterfaceTest.csproj
deleted file mode 100644
index 59ac818..0000000
--- a/TestUSBInterface/USBInterfaceTest.csproj
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- {E69C9EE3-BCA8-4E5D-8EFA-7EBD75B1087A}
- Exe
- Properties
- TestUSBInterface
- TestUSBInterface
- v4.5
- 512
-
-
- x86
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- x86
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {30432D4A-0128-48E7-ADCD-249D70323611}
- USBInterface
-
-
-
-
-
\ No newline at end of file
diff --git a/USBInterface/ArrayHelpers.cs b/USBInterface/ArrayHelpers.cs
index 66bcc5f..2ae0e4e 100644
--- a/USBInterface/ArrayHelpers.cs
+++ b/USBInterface/ArrayHelpers.cs
@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace USBInterface
+namespace USBInterface
{
public class ArrayHelpers
{
diff --git a/USBInterface/DeviceScanner.cs b/USBInterface/DeviceScanner.cs
index c81da8a..5344f96 100644
--- a/USBInterface/DeviceScanner.cs
+++ b/USBInterface/DeviceScanner.cs
@@ -1,77 +1,101 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using System.Text;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Threading;
namespace USBInterface
{
public class DeviceScanner
- {
- public event EventHandler DeviceArrived;
- public event EventHandler DeviceRemoved;
+ {
+ public class DeviceRemovedArgs
+ {
+ public DeviceRemovedArgs(string path)
+ {
+ Path = path;
+ }
+ public string Path { get; }
+ }
- public bool isDeviceConnected
+ public class DeviceArrivedArgs
{
- get { return deviceConnected; }
+ public DeviceArrivedArgs(string path)
+ {
+ Path = path;
+ }
+
+ public string Path { get; }
}
- // for async reading
- private object syncLock = new object();
- private Thread scannerThread;
- private volatile bool asyncScanOn = false;
+ public event EventHandler DeviceArrived;
+ public event EventHandler DeviceRemoved;
+
+ private readonly List _connectedDevices = new List();
- private volatile bool deviceConnected = false;
+ // for async reading
+ private readonly object _syncLock = new object();
+ private Thread _scannerThread;
+ private volatile bool _asyncScanOn;
- private int scanIntervalMillisecs = 10;
+ private int _scanIntervalMillisecs = 10;
public int ScanIntervalInMillisecs
{
- get { lock (syncLock) { return scanIntervalMillisecs; } }
- set { lock (syncLock) { scanIntervalMillisecs = value; } }
+ get { lock (_syncLock) { return _scanIntervalMillisecs; } }
+ set { lock (_syncLock) { _scanIntervalMillisecs = value; } }
}
- public bool isScanning
- {
- get { return asyncScanOn; }
- }
+ public bool IsScanning => _asyncScanOn;
- private ushort vendorId;
- private ushort productId;
+ private readonly ushort _vendorId;
+ private readonly ushort _productId;
// Use this class to monitor when your devices connects.
// Note that scanning for device when it is open by another process will return FALSE
// even though the device is connected (because the device is unavailiable)
- public DeviceScanner(ushort VendorID, ushort ProductID, int scanIntervalMillisecs = 100)
+ public DeviceScanner(ushort vendorId, ushort productId, int scanIntervalMillisecs = 100)
{
- vendorId = VendorID;
- productId = ProductID;
+ _vendorId = vendorId;
+ _productId = productId;
ScanIntervalInMillisecs = scanIntervalMillisecs;
}
// scanning for device when it is open by another process will return false
- public static bool ScanOnce(ushort vid, ushort pid)
+ public static List ScanOnce(ushort vid, ushort pid)
{
- return HidApi.hid_enumerate(vid, pid) != IntPtr.Zero;
+ var list = new List();
+
+ var pDev = HidApi.hid_enumerate(vid, pid);
+ while (pDev != IntPtr.Zero)
+ {
+ var dev = (HidDeviceInfo)Marshal.PtrToStructure(pDev, typeof(HidDeviceInfo));
+ list.Add(dev);
+ // freeing the enumeration releases the device,
+ // do it as soon as you can, so we dont block device from others
+ HidApi.hid_free_enumeration(pDev);
+ pDev = dev.next;
+ }
+ return list;
}
public void StartAsyncScan()
{
// Build the thread to listen for reads
- if (asyncScanOn)
+ if (_asyncScanOn)
{
// dont run more than one thread
return;
}
- asyncScanOn = true;
- scannerThread = new Thread(ScanLoop);
- scannerThread.Name = "HidApiAsyncDeviceScanThread";
- scannerThread.Start();
+ _asyncScanOn = true;
+ _scannerThread = new Thread(ScanLoop) {Name = "HidApiAsyncDeviceScanThread"};
+ _scannerThread.Start();
}
public void StopAsyncScan()
{
- asyncScanOn = false;
+ _asyncScanOn = false;
}
private void ScanLoop()
@@ -82,38 +106,32 @@ private void ScanLoop()
// The read has a timeout parameter, so every X milliseconds
// we check if the user wants us to continue scanning.
- while (asyncScanOn)
+ while (_asyncScanOn)
{
try
{
- IntPtr device_info = HidApi.hid_enumerate(vendorId, productId);
- bool device_on_bus = device_info != IntPtr.Zero;
- // freeing the enumeration releases the device,
- // do it as soon as you can, so we dont block device from others
- HidApi.hid_free_enumeration(device_info);
- if (device_on_bus && ! deviceConnected)
+ var deviceInfo = ScanOnce(_vendorId, _productId);
+
+ var newlyConnectedDevices = deviceInfo.Where(di => !_connectedDevices.Contains(di.path)).ToArray();
+ var removedDevicePaths = _connectedDevices.Where(cd => deviceInfo.All(di => di.path != cd)).ToArray();
+
+ foreach (var newlyConnectedDevice in newlyConnectedDevices)
{
- // just found new device
- deviceConnected = true;
- if (DeviceArrived != null)
- {
- DeviceArrived(this, EventArgs.Empty);
- }
+ DeviceArrived?.Invoke(this, new DeviceArrivedArgs(newlyConnectedDevice.path));
+ _connectedDevices.Add(newlyConnectedDevice.path);
}
- if (! device_on_bus && deviceConnected)
+
+ foreach (var removedDevicePath in removedDevicePaths)
{
- // just lost device connection
- deviceConnected = false;
- if (DeviceRemoved != null)
- {
- DeviceRemoved(this, EventArgs.Empty);
- }
+ DeviceRemoved?.Invoke(this, new DeviceRemovedArgs(removedDevicePath));
+ _connectedDevices.Remove(removedDevicePath);
}
}
catch (Exception e)
{
// stop scan, user can manually restart again with StartAsyncScan()
- asyncScanOn = false;
+ Console.WriteLine(e.ToString());
+ _asyncScanOn = false;
}
// when read 0 bytes, sleep and read again
Thread.Sleep(ScanIntervalInMillisecs);
diff --git a/USBInterface/HidApi.cs b/USBInterface/HidApi.cs
index ee9a0fc..03e068c 100644
--- a/USBInterface/HidApi.cs
+++ b/USBInterface/HidApi.cs
@@ -1,6 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
@@ -16,39 +14,39 @@ internal class HidApi
// find the library on all platforms becasue of different
// naming conventions.
// Just use hidapi and expect users to supply it in same folder as .exe
- public const string DLL_FILE_NAME = "hidapi";
+ public const string DllFileName = "hidapi";
/// Return Type: int
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl)]
public static extern int hid_init();
/// Return Type: int
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl)]
public static extern int hid_exit();
/// Return Type: hid_device_info*
///vendor_id: unsigned short
///product_id: unsigned short
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr hid_enumerate(ushort vendor_id, ushort product_id);
/// Return Type: void
///devs: struct hid_device_info*
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl)]
public static extern void hid_free_enumeration(IntPtr devs);
/// Return Type: hid_device*
///vendor_id: unsigned short
///product_id: unsigned short
///serial_number: wchar_t*
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr hid_open(ushort vendor_id, ushort product_id, [In] string serial_number);
/// Return Type: hid_device*
///path: char*
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr hid_open_path([In] string path);
@@ -56,7 +54,7 @@ internal class HidApi
///device: hid_device*
///data: unsigned char*
///length: size_t->unsigned int
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl)]
public static extern int hid_write(IntPtr device, [In] byte[] data, uint length);
@@ -65,7 +63,7 @@ internal class HidApi
///data: unsigned char*
///length: size_t->unsigned int
///milliseconds: int
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl)]
public static extern int hid_read_timeout(IntPtr device, [Out] byte[] buf_data, uint length, int milliseconds);
@@ -73,22 +71,22 @@ internal class HidApi
///device: hid_device*
///data: unsigned char*
///length: size_t->unsigned int
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl)]
public static extern int hid_read(IntPtr device, [Out] byte[] buf_data, uint length);
/// Return Type: int
///device: hid_device*
///nonblock: int
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl)]
- public extern static int hid_set_nonblocking(IntPtr device, int nonblock);
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl)]
+ public static extern int hid_set_nonblocking(IntPtr device, int nonblock);
/// Return Type: int
///device: hid_device*
///data: char*
///length: size_t->unsigned int
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl)]
public static extern int hid_send_feature_report(IntPtr device, [In] byte[] data, uint length);
@@ -96,21 +94,21 @@ internal class HidApi
///device: hid_device*
///data: unsigned char*
///length: size_t->unsigned int
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl)]
public static extern int hid_get_feature_report(IntPtr device, [Out] byte[] buf_data, uint length);
/// Return Type: void
///device: hid_device*
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl)]
- public extern static void hid_close(IntPtr device);
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl)]
+ public static extern void hid_close(IntPtr device);
/// Return Type: int
///device: hid_device*
///string: wchar_t*
///maxlen: size_t->unsigned int
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int hid_get_manufacturer_string(IntPtr device, StringBuilder buf_string, uint length);
@@ -118,7 +116,7 @@ internal class HidApi
///device: hid_device*
///string: wchar_t*
///maxlen: size_t->unsigned int
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int hid_get_product_string(IntPtr device, StringBuilder buf_string, uint length);
@@ -126,7 +124,7 @@ internal class HidApi
///device: hid_device*
///string: wchar_t*
///maxlen: size_t->unsigned int
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int hid_get_serial_number_string(IntPtr device, StringBuilder buf_serial, uint maxlen);
@@ -135,13 +133,13 @@ internal class HidApi
///string_index: int
///string: wchar_t*
///maxlen: size_t->unsigned int
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int hid_get_indexed_string(IntPtr device, int string_index, StringBuilder buf_string, uint maxlen);
/// Return Type: wchar_t*
///device: hid_device*
- [DllImport(DLL_FILE_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
+ [DllImport(DllFileName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern IntPtr hid_error(IntPtr device);
diff --git a/USBInterface/Properties/AssemblyInfo.cs b/USBInterface/Properties/AssemblyInfo.cs
deleted file mode 100644
index e9cedb0..0000000
--- a/USBInterface/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("USBInterface")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("Microsoft")]
-[assembly: AssemblyProduct("USBInterface")]
-[assembly: AssemblyCopyright("Copyright © Microsoft 2014")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("6945dc49-9902-4a76-84d4-6a6e2bd81813")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/USBInterface/ReportEventArgs.cs b/USBInterface/ReportEventArgs.cs
index 7fb3cad..0d98064 100644
--- a/USBInterface/ReportEventArgs.cs
+++ b/USBInterface/ReportEventArgs.cs
@@ -1,7 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
namespace USBInterface
{
@@ -13,6 +10,6 @@ public ReportEventArgs(byte[] data)
Data = data;
}
- public byte[] Data { get; private set; }
+ public byte[] Data { get; }
}
}
diff --git a/USBInterface/USBDevice.cs b/USBInterface/USBDevice.cs
index d56b3df..b3b144b 100644
--- a/USBInterface/USBDevice.cs
+++ b/USBInterface/USBDevice.cs
@@ -1,8 +1,5 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Text;
-using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Globalization;
using System.Threading;
@@ -16,52 +13,49 @@ public class USBDevice : IDisposable
public event EventHandler InputReportArrivedEvent;
public event EventHandler DeviceDisconnecedEvent;
- public bool isOpen
- {
- get { return DeviceHandle != IntPtr.Zero; }
- }
+ public bool IsOpen => _deviceHandle != IntPtr.Zero;
// If the read process grabs ownership of device
// and blocks (unable to get any data from device)
// for more than Timeout millisecons
// it will abandon reading, pause for readIntervalInMillisecs
// and try reading again.
- private int readTimeoutInMillisecs = 1;
+ private int _readTimeoutInMillisecs = 1;
public int ReadTimeoutInMillisecs
{
- get { lock (syncLock) { return readTimeoutInMillisecs; } }
- set { lock(syncLock) { readTimeoutInMillisecs = value; } }
+ get { lock (_syncLock) { return _readTimeoutInMillisecs; } }
+ set { lock(_syncLock) { _readTimeoutInMillisecs = value; } }
}
// Interval of time between two reads,
// during this time the device is free and
// we can write to it.
- private int readIntervalInMillisecs = 4;
+ private int _readIntervalInMillisecs = 4;
public int ReadIntervalInMillisecs
{
- get { lock (syncLock) { return readIntervalInMillisecs; } }
- set { lock(syncLock) { readIntervalInMillisecs = value; } }
+ get { lock (_syncLock) { return _readIntervalInMillisecs; } }
+ set { lock(_syncLock) { _readIntervalInMillisecs = value; } }
}
// for async reading
- private object syncLock = new object();
- private Thread readThread;
- private volatile bool asyncReadOn = false;
+ private readonly object _syncLock = new object();
+ private Thread _readThread;
+ private volatile bool _asyncReadOn;
// Flag: Has Dispose already been called?
// Marked as volatile because Dispose() can be called from another thread.
- private volatile bool disposed = false;
+ private volatile bool _disposed;
- private IntPtr DeviceHandle = IntPtr.Zero;
+ private IntPtr _deviceHandle;
// this will be the return buffer for strings,
// make it big, becasue by the HID spec (can not find page)
// we are allowed to request more bytes than the device can return.
- private StringBuilder pOutBuf = new StringBuilder(1024);
+ private readonly StringBuilder _pOutBuf = new StringBuilder(1024);
// This is very convinient to use for the 90% of devices that
// dont use ReportIDs and so have only one input report
- private int DefaultInputReportLength = -1;
+ private readonly int _defaultInputReportLength = -1;
// This only affects the read function.
// receiving / sending a feature report,
@@ -71,7 +65,7 @@ public int ReadIntervalInMillisecs
// the prefix byte is NOT inserted. On the other hand if the device uses
// Report IDs then when reading we must read +1 byte and byte 0
// of returned data array will be the Report ID.
- private bool hasReportIds = false;
+ private readonly bool _hasReportIds;
// HIDAPI does not provide any way to get or parse the HID Report Descriptor,
// This means you must know in advance what it the report size for your device.
@@ -79,21 +73,31 @@ public int ReadIntervalInMillisecs
//
// Serial Number is optional, pass null (do NOT pass an empty string) if it is unknown.
//
- public USBDevice(ushort VendorID
- , ushort ProductID
- , string serial_number
- , bool HasReportIDs = true
+ public USBDevice(ushort vendorId
+ , ushort productId
+ , string serialNumber
+ , bool hasReportIDs = true
+ , int defaultInputReportLen = -1)
+ {
+ _deviceHandle = HidApi.hid_open(vendorId, productId, serialNumber);
+ AssertValidDev();
+ _defaultInputReportLength = defaultInputReportLen;
+ _hasReportIds = hasReportIDs;
+ }
+
+ public USBDevice(string path
+ , bool hasReportIDs = true
, int defaultInputReportLen = -1)
{
- DeviceHandle = HidApi.hid_open(VendorID, ProductID, serial_number);
+ _deviceHandle = HidApi.hid_open_path(path);
AssertValidDev();
- DefaultInputReportLength = defaultInputReportLen;
- hasReportIds = HasReportIDs;
+ _defaultInputReportLength = defaultInputReportLen;
+ _hasReportIds = hasReportIDs;
}
private void AssertValidDev()
{
- if (DeviceHandle == IntPtr.Zero) throw new Exception("No device opened");
+ if (_deviceHandle == IntPtr.Zero) throw new Exception("No device opened");
}
public void GetFeatureReport(byte[] buffer, int length = -1)
@@ -103,7 +107,7 @@ public void GetFeatureReport(byte[] buffer, int length = -1)
{
length = buffer.Length;
}
- if (HidApi.hid_get_feature_report(DeviceHandle, buffer, (uint)length) < 0)
+ if (HidApi.hid_get_feature_report(_deviceHandle, buffer, (uint)length) < 0)
{
throw new Exception("failed to get feature report");
}
@@ -116,7 +120,7 @@ public void SendFeatureReport(byte[] buffer, int length = -1)
{
length = buffer.Length;
}
- if (HidApi.hid_send_feature_report(DeviceHandle, buffer, (uint)length) < 0)
+ if (HidApi.hid_send_feature_report(_deviceHandle, buffer, (uint)length) < 0)
{
throw new Exception("failed to send feature report");
}
@@ -132,12 +136,12 @@ private int ReadRaw(byte[] buffer, int length = -1)
{
length = buffer.Length;
}
- int bytes_read = HidApi.hid_read_timeout(DeviceHandle, buffer, (uint)length, readTimeoutInMillisecs);
- if (bytes_read < 0)
+ var bytesRead = HidApi.hid_read_timeout(_deviceHandle, buffer, (uint)length, _readTimeoutInMillisecs);
+ if (bytesRead < 0)
{
throw new Exception("Failed to Read.");
}
- return bytes_read;
+ return bytesRead;
}
// Meaning OutputReport
@@ -148,7 +152,7 @@ private void WriteRaw(byte[] buffer, int length = -1)
{
length = buffer.Length;
}
- if (HidApi.hid_write(DeviceHandle, buffer, (uint)length) < 0)
+ if (HidApi.hid_write(_deviceHandle, buffer, (uint)length) < 0)
{
throw new Exception("Failed to write.");
}
@@ -157,7 +161,7 @@ private void WriteRaw(byte[] buffer, int length = -1)
public string GetErrorString()
{
AssertValidDev();
- IntPtr ret = HidApi.hid_error(DeviceHandle);
+ var ret = HidApi.hid_error(_deviceHandle);
// I can not find the info in the docs, but guess this frees
// the ret pointer after we created a managed string object
// else this would be a memory leak
@@ -173,56 +177,56 @@ public string GetErrorString()
// buffer beforehand and just divide the capacity by 4.
public string GetIndexedString(int index)
{
- lock(syncLock)
+ lock(_syncLock)
{
AssertValidDev();
- if (HidApi.hid_get_indexed_string(DeviceHandle, index, pOutBuf, (uint)pOutBuf.Capacity / 4) < 0)
+ if (HidApi.hid_get_indexed_string(_deviceHandle, index, _pOutBuf, (uint)_pOutBuf.Capacity / 4) < 0)
{
throw new Exception("failed to get indexed string");
}
- return pOutBuf.ToString();
+ return _pOutBuf.ToString();
}
}
public string GetManufacturerString()
{
- lock (syncLock)
+ lock (_syncLock)
{
AssertValidDev();
- pOutBuf.Clear();
- if (HidApi.hid_get_manufacturer_string(DeviceHandle, pOutBuf, (uint)pOutBuf.Capacity / 4) < 0)
+ _pOutBuf.Clear();
+ if (HidApi.hid_get_manufacturer_string(_deviceHandle, _pOutBuf, (uint)_pOutBuf.Capacity / 4) < 0)
{
throw new Exception("failed to get manufacturer string");
}
- return pOutBuf.ToString();
+ return _pOutBuf.ToString();
}
}
public string GetProductString()
{
- lock (syncLock)
+ lock (_syncLock)
{
AssertValidDev();
- pOutBuf.Clear();
- if (HidApi.hid_get_product_string(DeviceHandle, pOutBuf, (uint)pOutBuf.Capacity / 4) < 0)
+ _pOutBuf.Clear();
+ if (HidApi.hid_get_product_string(_deviceHandle, _pOutBuf, (uint)_pOutBuf.Capacity / 4) < 0)
{
throw new Exception("failed to get product string");
}
- return pOutBuf.ToString();
+ return _pOutBuf.ToString();
}
}
public string GetSerialNumberString()
{
- lock (syncLock)
+ lock (_syncLock)
{
AssertValidDev();
- pOutBuf.Clear();
- if (HidApi.hid_get_serial_number_string(DeviceHandle, pOutBuf, (uint)pOutBuf.Capacity / 4) < 0)
+ _pOutBuf.Clear();
+ if (HidApi.hid_get_serial_number_string(_deviceHandle, _pOutBuf, (uint)_pOutBuf.Capacity / 4) < 0)
{
throw new Exception("failed to get serial number string");
}
- return pOutBuf.ToString();
+ return _pOutBuf.ToString();
}
}
@@ -233,14 +237,14 @@ public string Description()
, GetManufacturerString(), GetProductString(), GetSerialNumberString());
}
- public void Write(byte[] user_data)
+ public void Write(byte[] userData)
{
// so we don't read and write at the same time
- lock (syncLock)
+ lock (_syncLock)
{
- byte[] output_report = new byte[user_data.Length];
- Array.Copy(user_data, output_report, output_report.Length);
- WriteRaw(output_report);
+ var outputReport = new byte[userData.Length];
+ Array.Copy(userData, outputReport, outputReport.Length);
+ WriteRaw(outputReport);
}
}
@@ -254,19 +258,19 @@ public void Write(byte[] user_data)
// Output, Input, Feature reports.
public byte[] Read(int reportLen = -1)
{
- lock(syncLock)
+ lock(_syncLock)
{
- int length = reportLen;
+ var length = reportLen;
if (length < 0)
{
// when we have Report IDs and the user did not specify the reportLen explicitly
// then add an extra byte to account for the Report ID
- length = hasReportIds ? DefaultInputReportLength + 1 : DefaultInputReportLength;
+ length = _hasReportIds ? _defaultInputReportLength + 1 : _defaultInputReportLength;
}
- byte[] input_report = new byte[length];
- int read_bytes = ReadRaw(input_report);
- byte[] ret = new byte[read_bytes];
- Array.Copy(input_report, 0, ret, 0, read_bytes);
+ var inputReport = new byte[length];
+ var readBytes = ReadRaw(inputReport);
+ var ret = new byte[readBytes];
+ Array.Copy(inputReport, 0, ret, 0, readBytes);
return ret;
}
}
@@ -274,20 +278,19 @@ public byte[] Read(int reportLen = -1)
public void StartAsyncRead()
{
// Build the thread to listen for reads
- if (asyncReadOn)
+ if (_asyncReadOn)
{
// dont run more than one read
return;
}
- asyncReadOn = true;
- readThread = new Thread(ReadLoop);
- readThread.Name = "HidApiReadAsyncThread";
- readThread.Start();
+ _asyncReadOn = true;
+ _readThread = new Thread(ReadLoop) {Name = "HidApiReadAsyncThread"};
+ _readThread.Start();
}
public void StopAsyncRead()
{
- asyncReadOn = false;
+ _asyncReadOn = false;
}
private void ReadLoop()
@@ -298,25 +301,22 @@ private void ReadLoop()
// The read has a timeout parameter, so every X milliseconds
// we check if the user wants us to continue reading.
- while (asyncReadOn)
+ while (_asyncReadOn)
{
try
{
- byte[] res = Read();
+ var res = Read();
// when read >0 bytes, tell others about data
- if (res.Length > 0 && this.InputReportArrivedEvent != null)
+ if (res.Length > 0)
{
- InputReportArrivedEvent(this, new ReportEventArgs(res));
+ InputReportArrivedEvent?.Invoke(this, new ReportEventArgs(res));
}
}
catch (Exception)
{
// when read <0 bytes, means an error has occurred
// stop device, break from loop and stop this thread
- if (this.DeviceDisconnecedEvent != null)
- {
- DeviceDisconnecedEvent(this, EventArgs.Empty);
- }
+ DeviceDisconnecedEvent?.Invoke(this, EventArgs.Empty);
// call the dispose method in separate thread,
// otherwise this thread would never get to die
new Thread(Dispose).Start();
@@ -325,7 +325,7 @@ private void ReadLoop()
// when read 0 bytes, sleep and read again
// We must sleep for some time to allow others
// to write to the device.
- Thread.Sleep(readIntervalInMillisecs);
+ Thread.Sleep(_readIntervalInMillisecs);
}
}
@@ -339,49 +339,42 @@ public void Dispose()
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
- if (disposed)
+ if (_disposed)
{
return;
}
if (disposing)
{
// Free any other managed objects here.
- if (asyncReadOn)
+ if (_asyncReadOn)
{
- asyncReadOn = false;
- readThread.Join(readTimeoutInMillisecs);
- if (readThread.IsAlive)
+ _asyncReadOn = false;
+ _readThread.Join(_readTimeoutInMillisecs);
+ if (_readThread.IsAlive)
{
- readThread.Abort();
+ _readThread.Abort();
}
}
}
// Free any UN-managed objects here.
// so we are not reading or writing as the device gets closed
- lock (syncLock)
+ lock (_syncLock)
{
- if (isOpen)
+ if (IsOpen)
{
- HidApi.hid_close(DeviceHandle);
- DeviceHandle = IntPtr.Zero;
+ HidApi.hid_close(_deviceHandle);
+ _deviceHandle = IntPtr.Zero;
}
}
HidApi.hid_exit();
// mark object as having been disposed
- disposed = true;
+ _disposed = true;
}
~USBDevice()
{
Dispose(false);
}
-
- private string EncodeBuffer(byte[] buffer)
- {
- // the buffer contains trailing '\0' char to mark its end.
- return Encoding.Unicode.GetString(buffer).Trim('\0');
- }
-
}
}
diff --git a/USBInterface/USBInterface.csproj b/USBInterface/USBInterface.csproj
index 28ed6a0..17d3b29 100644
--- a/USBInterface/USBInterface.csproj
+++ b/USBInterface/USBInterface.csproj
@@ -1,59 +1,5 @@
-
-
-
+
- Debug
- AnyCPU
- {30432D4A-0128-48E7-ADCD-249D70323611}
- Library
- Properties
- USBInterface
- USBInterface
- v4.0
- 512
-
+ netstandard2.0
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
- x86
-
-
- true
- bin\Release\
- TRACE
- prompt
- 4
- x86
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/USBInterface/hid_device_info.cs b/USBInterface/hid_device_info.cs
new file mode 100644
index 0000000..0948065
--- /dev/null
+++ b/USBInterface/hid_device_info.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace USBInterface
+{
+ // Used from https://stackoverflow.com/questions/29298336/using-a-c-struct-in-c-sharp
+ [StructLayout(LayoutKind.Sequential)]
+ public struct HidDeviceInfo
+ {
+ [MarshalAs(UnmanagedType.LPStr)]
+ public String path;
+ public ushort vendor_id;
+ public ushort product_id;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public String serial_number;
+ public ushort release_number;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public String manufacturer_string;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public String product_string;
+ public ushort usage_page;
+ public ushort usage;
+ public int interface_number;
+ public IntPtr next;
+ };
+}
\ No newline at end of file