From 2c589f1b1ed8b3c17144b6e7c4f0a023001f333b Mon Sep 17 00:00:00 2001 From: badcel <1218031+badcel@users.noreply.github.com> Date: Sun, 9 Oct 2022 13:52:02 +0200 Subject: [PATCH] Support connection via serial number --- src/HidApi.Net.Tests/StringTests.cs | 13 ++++++++++++ src/HidApi.Net/Device.cs | 16 ++++++++++++++- src/HidApi.Net/HidApi.Net.csproj | 2 +- src/HidApi.Net/Internal/NativeMethods.cs | 13 +++++++++++- .../Internal/NullTerminatedString.cs | 20 +++++++++++++++++++ src/HidApi.Net/Internal/Unicode.cs | 9 +++++++++ src/HidApi.Net/Internal/Utf32.cs | 9 +++++++++ src/HidApi.Net/Internal/WCharT.cs | 14 +++++++++++++ 8 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 src/HidApi.Net/Internal/NullTerminatedString.cs diff --git a/src/HidApi.Net.Tests/StringTests.cs b/src/HidApi.Net.Tests/StringTests.cs index a092ce3..9a8b6f1 100644 --- a/src/HidApi.Net.Tests/StringTests.cs +++ b/src/HidApi.Net.Tests/StringTests.cs @@ -53,4 +53,17 @@ public void TestUtf32() } } } + + + [TestMethod] + public unsafe void CanCreateNullTerminatedString() + { + var result = "MyString"; + var nullTerminatedString = WCharT.CreateNullTerminatedString(result); + fixed (byte* ptr = nullTerminatedString) + { + var r = WCharT.GetString(ptr); + r.Should().Be(result); + } + } } diff --git a/src/HidApi.Net/Device.cs b/src/HidApi.Net/Device.cs index 97718c9..16ebdf7 100644 --- a/src/HidApi.Net/Device.cs +++ b/src/HidApi.Net/Device.cs @@ -15,7 +15,21 @@ public class Device : IDisposable /// product id of target device public Device(ushort vendorId, ushort productId) { - handle = NativeMethods.Open(vendorId, productId, string.Empty); + handle = NativeMethods.Open(vendorId, productId, NullTerminatedString.Empty); + + if (handle.IsInvalid) + HidException.Throw(handle); + } + + /// + /// Connects to a given device. + /// + /// Vendor id of target device + /// Product id of target device + /// Serial number of target device + public Device(ushort vendorId, ushort productId, string serialNumber) + { + handle = NativeMethods.Open(vendorId, productId, WCharT.CreateNullTerminatedString(serialNumber)); if (handle.IsInvalid) HidException.Throw(handle); diff --git a/src/HidApi.Net/HidApi.Net.csproj b/src/HidApi.Net/HidApi.Net.csproj index 2453527..b0c8e30 100644 --- a/src/HidApi.Net/HidApi.Net.csproj +++ b/src/HidApi.Net/HidApi.Net.csproj @@ -26,7 +26,7 @@ - + diff --git a/src/HidApi.Net/Internal/NativeMethods.cs b/src/HidApi.Net/Internal/NativeMethods.cs index 1018f98..480af2f 100644 --- a/src/HidApi.Net/Internal/NativeMethods.cs +++ b/src/HidApi.Net/Internal/NativeMethods.cs @@ -36,6 +36,17 @@ private static IntPtr Resolve(string libraryName, Assembly assembly, DllImportSe throw new Exception($"Could not find hidapi library tried: {string.Join(", ", libraries)}"); } + public static DeviceSafeHandle Open(ushort vendorId, ushort productId, NullTerminatedString serialNumber) + { + unsafe + { + fixed(byte* ptr = serialNumber) + { + return Open(vendorId, productId, ptr); + } + } + } + public static int Write(DeviceSafeHandle device, ReadOnlySpan data) { return Write(device, ref MemoryMarshal.GetReference(data), (nuint) data.Length); @@ -99,7 +110,7 @@ public static int GetInputReport(DeviceSafeHandle device, ReadOnlySpan dat public static extern unsafe void FreeEnumeration(NativeDeviceInfo* devices); [DllImport(Library, EntryPoint = "hid_open")] - public static extern DeviceSafeHandle Open(ushort vendorId, ushort productId, [MarshalAs(UnmanagedType.LPWStr)] string serialNumber); + private static extern unsafe DeviceSafeHandle Open(ushort vendorId, ushort productId, byte* serialNumber); [DllImport(Library, EntryPoint = "hid_open_path")] public static extern DeviceSafeHandle OpenPath([MarshalAs(UnmanagedType.LPStr)] string path); diff --git a/src/HidApi.Net/Internal/NullTerminatedString.cs b/src/HidApi.Net/Internal/NullTerminatedString.cs new file mode 100644 index 0000000..023a3f1 --- /dev/null +++ b/src/HidApi.Net/Internal/NullTerminatedString.cs @@ -0,0 +1,20 @@ +namespace HidApi; + +internal readonly ref struct NullTerminatedString +{ + public static NullTerminatedString Empty => new (); + + private readonly ReadOnlySpan data; + + public NullTerminatedString() + { + data = ReadOnlySpan.Empty; + } + + internal NullTerminatedString(ref byte[] str) + { + data = str; + } + + public ref readonly byte GetPinnableReference() => ref data.GetPinnableReference(); +} diff --git a/src/HidApi.Net/Internal/Unicode.cs b/src/HidApi.Net/Internal/Unicode.cs index a2f0ba7..e4f8db1 100644 --- a/src/HidApi.Net/Internal/Unicode.cs +++ b/src/HidApi.Net/Internal/Unicode.cs @@ -4,6 +4,15 @@ namespace HidApi; internal static class Unicode { + public static NullTerminatedString CreateNullTerminatedString(string str) + { + var src = Encoding.Unicode.GetBytes(str); + var dest = new byte[src.Length + sizeof(ushort)]; + Array.Copy(src, dest, src.Length); + + return new NullTerminatedString(ref dest); + } + public static ReadOnlySpan CreateBuffer(int size) { return new byte[size * sizeof(ushort)]; diff --git a/src/HidApi.Net/Internal/Utf32.cs b/src/HidApi.Net/Internal/Utf32.cs index 23b4846..7a03487 100644 --- a/src/HidApi.Net/Internal/Utf32.cs +++ b/src/HidApi.Net/Internal/Utf32.cs @@ -4,6 +4,15 @@ namespace HidApi; internal static class Utf32 { + public static NullTerminatedString CreateNullTerminatedString(string str) + { + var src = Encoding.UTF32.GetBytes(str); + var dest = new byte[src.Length + sizeof(uint)]; + Array.Copy(src, dest, src.Length); + + return new NullTerminatedString(ref dest); + } + public static ReadOnlySpan CreateBuffer(int size) { return new byte[size * sizeof(uint)]; diff --git a/src/HidApi.Net/Internal/WCharT.cs b/src/HidApi.Net/Internal/WCharT.cs index 55d4c76..777bdc5 100644 --- a/src/HidApi.Net/Internal/WCharT.cs +++ b/src/HidApi.Net/Internal/WCharT.cs @@ -46,4 +46,18 @@ public static ReadOnlySpan CreateBuffer(int size) throw new Exception("Unsupported platform to create a buffer"); } + + public static NullTerminatedString CreateNullTerminatedString(string str) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + return Utf32.CreateNullTerminatedString(str); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return Unicode.CreateNullTerminatedString(str); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + return Utf32.CreateNullTerminatedString(str); + + throw new Exception("Unsupported platform to create a null terminated string"); + } }