diff --git a/Analyzer/EonaCat.HID.Analyzer.csproj b/Analyzer/EonaCat.HID.Analyzer.csproj
index 8bd9a63..1258cdd 100644
--- a/Analyzer/EonaCat.HID.Analyzer.csproj
+++ b/Analyzer/EonaCat.HID.Analyzer.csproj
@@ -96,13 +96,13 @@
-
- {9e8f1d50-74ea-4c60-bd5c-ab2c5b53bc66}
- EonaCat.HID
-
+
-
+
+ {00403bd6-7a26-4971-29d3-8a7849aac770}
+ EonaCat.HID
+
VID {0:X4}, PID {0:X4}", e.Device.VendorId, e.Device.ProductId);
+ AppendEventLog(str);
+ }
+ catch (Exception ex)
+ {
+ PopupException(ex.Message);
+ }
+ }
+
+ private void Hid_Inserted(object sender, HidEventArgs e)
+ {
+ try
+ {
+ var str = string.Format("Inserted Device --> VID {0:X4}, PID {0:X4}", e.Device.VendorId, e.Device.ProductId);
+ AppendEventLog(str, Color.Orange);
}
catch (Exception ex)
{
@@ -235,63 +316,30 @@ namespace EonaCat.HID.Analyzer
}
}
- private void ButtonReadInput_Click(object sender, EventArgs e)
+ private async void ButtonReadInput_Click(object sender, EventArgs e)
{
try
{
- var len = _device.Capabilities.InputReportByteLength;
+ if (_device == null)
+ {
+ AppendEventLog("No device connected. Please select a device and click 'Connect'.", Color.Red);
+ return;
+ }
+
+ var len = (int)_device.InputReportByteLength;
if (len <= 0)
{
throw new Exception("This device has no Input Report support!");
}
- _device.ReadReport(Device_InputReportReceived);
- AppendEventLog("Hid Input Report Callback Started.");
- }
- catch (Exception ex)
- {
- PopupException(ex.Message);
- }
- }
-
- private void ButtonWriteOutput_Click(object sender, EventArgs e)
- {
- try
- {
- var hidReportId = byte.Parse(comboBoxReportId.Text);
- var buf = ByteHelper.HexStringToByteArray(textBoxWriteData.Text);
-
- var report = _device.CreateReport();
- if (buf.Length > report.Data.Length)
+ var buffer = await _device.ReadInputReportAsync();
+ if (buffer.Length < 2)
{
- throw new Exception("Output Report Length Exceed");
+ AppendEventLog("Received report is too short to contain a valid Report ID.", Color.Red);
+ return;
}
-
- report.ReportId = hidReportId;
- Array.Copy(buf, report.Data, buf.Length);
- _device.WriteReport(report, WriteReportTimeout);
- var str = string.Format("Tx Output Report [{0}] --> ID:{1}, {2}", report.Data.Length + 1, report.ReportId, ByteHelper.ByteArrayToHexString(report.Data));
- AppendEventLog(str, Color.DarkGreen);
- }
- catch (Exception ex)
- {
- PopupException(ex.Message);
- }
- }
-
- private void ButtonReadFeature_Click(object sender, EventArgs e)
- {
- try
- {
- var hidReportId = byte.Parse(comboBoxReportId.Text);
- var len = _device.Capabilities.FeatureReportByteLength;
- if (len <= 0)
- {
- throw new Exception("This device has no Feature Report support!");
- }
-
- _device.ReadFeatureData(out byte[] buf, hidReportId);
- var str = string.Format("Rx Feature Report [{0}] <-- {1}", buf.Length, ByteHelper.ByteArrayToHexString(buf));
+
+ var str = string.Format("Rx Input Report [{0}] <-- {1}", buffer.Length, BitConverter.ToString(buffer));
AppendEventLog(str, Color.Blue);
}
catch (Exception ex)
@@ -300,21 +348,70 @@ namespace EonaCat.HID.Analyzer
}
}
- private void ButtonWriteFeature_Click(object sender, EventArgs e)
+ private async void ButtonWriteOutput_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ byte hidReportId = byte.Parse(comboBoxReportId.Text.Trim());
+ byte[] buf = ByteHelper.HexStringToByteArray(textBoxWriteData.Text.Trim());
+
+ // Combine report ID and buffer
+ byte[] outputReport = new byte[buf.Length + 1];
+ outputReport[0] = hidReportId;
+ Array.Copy(buf, 0, outputReport, 1, buf.Length);
+
+ try
+ {
+ await _device.WriteOutputReportAsync(outputReport);
+ AppendEventLog($"Output report sent: {BitConverter.ToString(outputReport)}", Color.DarkGreen);
+ }
+ catch (Exception ex)
+ {
+ AppendEventLog("Write failed: " + ex.Message, Color.DarkRed);
+ }
+ }
+ catch (Exception ex)
+ {
+ PopupException("Error preparing output report: " + ex.Message);
+ }
+ }
+
+ private async void ButtonReadFeature_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ var hidReportId = byte.Parse(comboBoxReportId.Text);
+ var len = _device.FeatureReportByteLength;
+ if (len <= 0)
+ {
+ throw new Exception("This device has no Feature Report support!");
+ }
+
+ var buffer = await _device.GetFeatureReportAsync(hidReportId);
+ var str = string.Format("Rx Feature Report [{0}] <-- {1}", buffer.Length, ByteHelper.ByteArrayToHexString(buffer));
+ AppendEventLog(str, Color.Blue);
+ }
+ catch (Exception ex)
+ {
+ PopupException(ex.Message);
+ }
+ }
+
+ private async void ButtonWriteFeature_Click(object sender, EventArgs e)
{
try
{
var hidReportId = byte.Parse(comboBoxReportId.Text);
var buf = ByteHelper.HexStringToByteArray(textBoxWriteData.Text);
- var len = _device.Capabilities.FeatureReportByteLength;
+ var len = _device.FeatureReportByteLength;
if (buf.Length > len)
{
throw new Exception("Write Feature Report Length Exceed");
}
Array.Resize(ref buf, len);
- _device.WriteFeatureData(buf);
+ await _device.SendFeatureReportAsync(buf);
var str = string.Format("Tx Feature Report [{0}] --> {1}", buf.Length, ByteHelper.ByteArrayToHexString(buf));
AppendEventLog(str, Color.DarkGreen);
}
@@ -328,53 +425,15 @@ namespace EonaCat.HID.Analyzer
{
try
{
- if (dataGridView1.SelectedRows.Count <= 0) return;
+ if (dataGridView1.SelectedRows.Count <= 0)
+ {
+ return;
+ }
+
var index = dataGridView1.SelectedRows[0].Index;
- var devinfo = _deviceList[index].Info;
- toolStripTextBoxVidPid.Text = string.Format("{0:X4}:{1:X4}", devinfo.VendorId, devinfo.ProductId);
- }
- catch (Exception ex)
- {
- PopupException(ex.Message);
- }
- }
-
- private void HidDevice_Inserted()
- {
- try
- {
- var str = string.Format("Inserted Device --> VID {0:X4}, PID {0:X4}", _device.Info.VendorId, _device.Info.ProductId);
- AppendEventLog(str, Color.Orange);
- _device.ReadReport(Device_InputReportReceived);
- }
- catch (Exception ex)
- {
- PopupException(ex.Message);
- }
- }
-
- private void HidDevice_Removed()
- {
- try
- {
- var str = string.Format("Removed Device --> VID {0:X4}, PID {0:X4}", _device.Info.VendorId, _device.Info.ProductId);
- AppendEventLog(str);
- }
- catch (Exception ex)
- {
- PopupException(ex.Message);
- }
- }
-
- private void Device_InputReportReceived(Report report)
- {
- try
- {
- if (!_device.IsConnected || !_device.IsOpen || report.Data == null) return;
- var str = $"Rx Input Report [{report.Data.Length + 1}] <-- ID:{report.ReportId}, {ByteHelper.ByteArrayToHexString(report.Data)}";
- AppendEventLog(str, Color.Blue);
- _device.ReadReport(Device_InputReportReceived);
+ var info = _deviceList.ElementAt(index);
+ toolStripTextBoxVidPid.Text = string.Format("{0:X4}:{1:X4}", info.VendorId, info.ProductId);
}
catch (Exception ex)
{
@@ -384,7 +443,12 @@ namespace EonaCat.HID.Analyzer
private void dataGridView1_DoubleClick(object sender, EventArgs e)
{
- ConnectToSelectedDevice();
+ ConnectToSelectedDeviceAsync().ConfigureAwait(false);
+ }
+
+ private void toolStripTextBoxVidPid_Enter(object sender, EventArgs e)
+ {
+ ToolStripButtonFilter_Click(sender, e);
}
}
}
\ No newline at end of file
diff --git a/Analyzer/MainForm.resx b/Analyzer/MainForm.resx
index e5e3c96..173f685 100644
--- a/Analyzer/MainForm.resx
+++ b/Analyzer/MainForm.resx
@@ -151,60 +151,59 @@
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAI4SURBVDhPjZNPaNNQHMcfjNGBVTpB2GUOtyDYwzbZsKV2
- fUkKJZd6292Tf27CDh6zHSxpCoJuDlrYLuqlF1FhBz20ox62NdU6JzotzDavzYpddy9ofL+Xl9l1CvvC
- j+R93+/3ye/3kqD/KTcy4sv5/d7tcHgQgtunUxahvrejlw/zl4R4ORi68+Ha9S/g10QRs4ReaVvNULJo
- 3YfQjPpF8HJjY8KaIHi+KYrn0/TMcA1jgYhyh2DpmTE11c8KXam5vQF9s1HXDctOFS2R2ydEMJ6kEIuI
- 0lNuOaKFs4mNekfftAoAaH3PLLc/z9vtSsaJ4s3d6vZddg4c0iGSdIMVg5KGtaUZjcdOJ82hg0o6dwxQ
- vmcflOfe8HRkYmmJYHmdLR68b1xIbJi/NWM/yAyqI0DpthP0Hjy+TQE4+APjX7VY7DyiLQdgdnWn6eX7
- DoAXupBuQEtRztExbBKJXkV6aX8cAMl3P8/y/b+ALkg3YA9jHwBMSZpAqbJ1BkaATvj+ccDOwokR6AGG
- YISq+4HRDvK60VhkCyoGcA9w96Hd/jjHAPTJQzQGTFFeJhE5z9MRSpWsuPMaySSsW5XMkyMAD/BMHBVN
- USqw1xiR46zYlV600gmDWC7kXwIAO7yZCIFOuO0obdj9dIwV6CRRagrTq+vDyqM1j0DDr74UIKcajY7S
- T1mDqEtSjBX2iv4LYbgGVvNfAyuFW+MLr+JX5l8fzmazfSzhtAo/LwxC+NWsd0J94eN2jxD6A3Zieio6
- Qs3XAAAAAElFTkSuQmCC
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIqSURBVDhPfZJNaBNBFMcXSknBKKkg9FKL7SKSQ1tpMSEm
+ mZ0NhFzirXdPftyEHjxueohsNiDUj0IC7UW95CIq5NDLpkQo7U401hatBkKys5kG0/Qe0JW3ycZsWvuH
+ P8u89+bHe2+W4/4jdWrKo3q97r1gcBw8nD9XOY4b2Zy+flK4xsfL/sCDz7duf4N4XRDQcK0lebcZSGns
+ MVgmxlWIqTMzfJ7nXT9iMdfXxdBkHSGeCmKHIvyaLCyMOgCSWh1TdhqGQpiZ1pjgSA6IIjRPBZFRAb9y
+ JBTClpLbRkfZYUUAtH5m19oHCbNdyXat3T2s7T209tCDdCjGd/qAFGG7Mmk863bSnDiuZFQHoPzIPC4v
+ b9r1OsIvKBK3rEPyU+NKcrv+RyZHfrugDyjd7/ogYULsHwD5qwj9rkejl7m0xnwwu7TfdDsAvYs2ZBDQ
+ isUuUUE0aThyk1NKR7MASH38dfEUYAAyCKgi5AGAjvEcly6zCzACdHImYH/l1AgU4wCMULN/MIWwgkIa
+ zx0Ae4GHT832l2ULUEVooorQmC6IazQsFux6Ll1i8e4z0nk4tyrZl31AzxDTUUTQBVy0njEsxvsAkKKx
+ TJJQZkPOEgCs5YVCFDpxJDPEHFVIYx06eVJq8osbW5Ox1byLX827vNI7Hmpqkcg0RVgGGxhHHQBbMjGC
+ 8PVtFL771ov3Zlfex28kPpws5XIjw7XnKvimOA72Sjn3nPTWM5y39RdV/noYsphYLAAAAABJRU5ErkJg
+ gg==
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIzSURBVDhPY5BuvNMjXXPjhWTJubsiuUffCOccfS2Re8qe
- AQrEiy8oihWcuSGeeXw2VAgVSNXfOSBdde2/ZNml/+J5J8FYJPtEAVSaASjeDJITyjj8HSqECggZIFF2
- uQQkJ5Zy+DNUCBUQMkC0/gqPVPqWU6K5J2KhQqiAkAEgYOTsHW/i6uNr4uytZOjoJc9QX88ElWJgkGm8
- u1+6GtUA8ZzjeVBpODBy9XUCGpAGwsbO3lOMjY1ZwRJSjXecpWpvLJXL2bFcPPfoWrHck6vEM46KgeTm
- 3/9vUH/lu0r9pe9KyNg1ubTQu3GaO9gAGDB19U0wd/dXgHIZFt3/cbj3xq//jVcwcd2Fr/9Tjn/+m3zq
- RyVUOaYByx/9eJ85ddV/n8Ts/3bhySjYwMnnf9KqU/9jT/zYD1UOCihfHyM3b2MoF2xAclP/f6fAiP/J
- 1r7/5xv5/V9m4Pffy8Hnv5aF3f+Q3mX/4098PwBVzsBg5uHBBwyc7tDQUGYQH9mADjO//9+VAv5/VQ74
- H2vri90AEDBx9FM3dvFpM3Hxrurfc+kTzABTF5//iwz9/leZ+/03AbJxGoAMQC5IaZv23zU4GqwJGWua
- 2f6PmrmNsAENl378L9t9+3/5wccIfODR/8JtN/6XXviJ34Cl9368bbz667/FpHv/Y7e8/e846+H/0NUv
- /3svfgbGJed//o89/mMPVDkmmH/nZ9+k27+eNF35dRcY9yi4/vKve8mnfzxJPv3DCwAg0ohxfV2WKwAA
- AABJRU5ErkJggg==
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIoSURBVDhPY5BuvNMjXXPjhWTJubsiuUfeCOccfS2Re8qe
+ AQrEiy8oihWcuSGeeXw2TAwFSNXfOSBdde2/ZNml/+J5J8FYJPtEAUxesuxSM0hOMOPwd1SdUEDIAImy
+ yyUgObHkw59RdUIBIQNE66/wSKVvOSWaeyIWVScUEDIABIycveNNXH18TZy9lQwdveQZ6uuZ4JIyjXf3
+ S1ejGiCeczwP2QAQMHL1dTJx9k4DYWNn7ynGxsasYAmpxjvOUrU3lsrl7Fgunnt0rVjuyVXiGUfFQHLz
+ 7/83qL/yXaX+0nclZOyaXFro3TjNHcUGU1ffBHN3fwUYf9H9H4d7b/z633gFE9dd+Po/+dinv8mnflTi
+ NGD5o+/vM6eu+u+TmP3fLjwZBRs4+fxPWnXqf+yJH/vhBhg5+/oYuXkbIxuQ3NT/3ykw4n+yte//+UZ+
+ /5cZ+P33cvD5r2Vh9z+kd9n/+BPfD8ANMPPw4DN29u4ODQ1lRjegw8zv/3elgP9flQP+x9r6YjcABEwc
+ /dSNXXzaTFy8q/p3X/wEM8DUxef/IkO//1Xmfv9NXHC4AB2AXJDSNu2/a3A0WBMy1jSz/R81cxthAxou
+ /fhftvv2//KDjxH4wKP/hdtu/C+98BO/AUvufn/bePXXf4tJ9/7Hbnn733HWw/+hq1/+9178DIxLzv/8
+ H3v8xx50fXAw/87Pvkm3fz1puvLrbiMarr/8617y6R9Pkk//8AIABXiIYedL+BwAAAAASUVORK5CYII=
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJSSURBVDhPY8AGbPa9SLPZ+yISysUP0upncmWU93anV/ZZ
- Q4UYbPY+e2qz68UNKBc/SC/vXZ9Z0fs/taTrc1Z5jw1IzHrPiy6bPS/qwAoIgczyngUgA5ANsTj2iHP2
- jl6huZOq3/V21/04sqnEDaocE9TX17MAvbAaZkhUbsOv1OLGvKkTar/BxNra6v/9Op8O9yIGABmSVta9
- Jja35X9Yet3/lJIOsEYQLq3r+H9va+T/n9eL3vx71KoD1YIJ4uvrOSKz65+kFKNqvr3Q5f+fqwX//z9q
- /f/7XuOXfw+bw6FaUMGV/Vk8kyfUwZ1dWtv+/97m8P+/L+X8/3e/EWwAArcs+PewSRuqFQIm9VV9Rbb5
- 7qbQ/183OQE1N6BpRuB/D1vioNoZGIpqOxHOXuD8//eV/P9/b1X//3u9DK4BxP9zuRDJgDZE7GxaXLa7
- rB7obGCA/b6Y/f/vzUq4Qjh+2PL/5+Ho/993+///usPn3f+n9VxQ7RDw9mja/B+Xi/5haATiP1dL/v88
- kQhm/77X+hVrjNjvfy8AdFY8UMFfuOYHTcBwaAKzf51O/f9tmyfQG/mZrjufc9vv/88C1crA4LL7Hb/N
- 3udP7Pe/lAA6NQFmyL+79f+/bfcC+r/q/+9HrT//3aouA6m33vN8ms3uF0VgzSBgs+d5BDAH/rfa8yID
- xP/3oNnpz6PWs78fNf76db/+07ddfpeBLjEEyXlsu8VusfvFO6u9L86C+GAQuuo/M9CAbfb773NAhcDg
- //96pv//VzFDuXAAzHAFtvte+AMAGv7JUh+nLuoAAAAASUVORK5CYII=
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJPSURBVDhPdZH/SxNhHMdHCpkFRT/1P9Qv/VS4i4FZlET+
+ ZDaiLJj7opk259Ya2zPnTm1uoURBRLToNwuCRcvcnfuhLwQhkVOsxTzv0PYEtaDadLft+cQtdttu6w0f
+ uOdzn9free4elapOqHmsp1isVfbrRo/uNhttgUmD/aa61KPYrxvUHP5UPfmfGGyBp6ZrAdBZfL97bX5K
+ 6qkZ7KMY7FLO1o3J5g9KgkrJ4bfCjnuzgb33px0pv8+19TpkOa7k5CCEGo22wOOSRNuPxJ6hkSu3p5yZ
+ Uo+mERE/GORPrIkk0Vsnn5zv98IZgwt0lokiKNWwawJWn2tha8X8nQj0ASUrpxuhJm0fWtcNVcNfHrZB
+ fnkQQKBBTLj/EH60S8kWsxTt3XVryiUfe9g5DqvPuiC3eBkIN1IUlMsbJLxnf5VgOmBPV+6cCHVCOtQK
+ hHMr4HIR3ntBFpidN8rHDh6F3NIAFOIOKKxYZUBa52NXKwRj5dsJPbJGrGi8+MNyH/ug8NlesyPwXsi+
+ OgebkQ5Iz55KwQZqlgVSfrzRP9hcNJMaUKAhv2yB7LtLxWcxQafr3ogm+nMP4ce6xQRdkOE1DxDO8w98
+ 3wOZ8EnIxwZMx14md2qi0CjDbZHUbopNrmui3/YB771YkpAEgsyLdijEr4Mo0FkSd1ileTWTvENFsFkW
+ UEzyLMViaGGwUVqTtdHWvEAviIJbzHLoV2budIxwnoPSuxPh+PZDEZxqYfGCLOicgQaKxWFNlGuSmyqV
+ CgBtA5hpqOxJUTN48Mg87vgL5YHJNRvDXwIAAAAASUVORK5CYII=
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEZSURBVDhPY6AKmFApJh7mpvHV1NT0PzE4zE3967RicTGo
- dgaGiZUK9h5Ohv+/7uL8//8gO178dRfXf5DaKeUKdlDtEJAfo361KlUVqyYY/neA/X9Fsur/vFiNS1Bt
- CDCzXpIrwl/n04pWaayaQXhJk8z/IF/1LxNzhfig2lDBxDJ5C0d7w7/nFwhiaL60SPC/A1AOw+nooCtP
- oczL2eD/683ccM1vt3L/B4m15yi0QZXhByVJKocL4tThBuTFaPwviVc5DJUmDKbUi/KYm5vCDQCxQWEE
- lSYM6usZmEDxDTMAxIZKEQcGlwGfdnD9t7QwId0AkKY9k8X/+7kZ/C9LVDkClSIeVCSq7o/x0frQma9Q
- DhWiBWBgAAClAOU+yA6HfwAAAABJRU5ErkJggg==
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEXSURBVDhPY2CgBphQKSYe5qr+1dTU9D8xOMxV7eu0YnEx
+ uAETKxXsPZwM/3/dxfn//0F2vPjrLq7/ILVTyhXsUFyRH6N+tSpVFUMDMv53gP1/RbLq/7xYjUsomkFg
+ Zr0kV4S/zqcVrdIYGmF4SZPM/0BftS8Tc4X40PWDwcQyeQsHe8O/5xcIYmi+tEjwv7294V8Mp6ODrjyF
+ Mi9ng/+vN3PDNb/dyv0fJNaeo9CGrh4rKElSOVwQpw43IC9G439JvMphdHU4wZR6UR5zc1O4ASA2KIzQ
+ 1eEE9fUMTKD4hhkAYqOrwQsGlwGfdnD9t7QwId0AkKY9k8X/+7kZ/C9LVDmCroYgqEhU3R/jo/WhM1+h
+ HF2OqgAAizrlNjwLhaIAAAAASUVORK5CYII=
@@ -213,12 +212,12 @@
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAERSURBVDhPrZDbSgJRGIXnpewd6jXsjSQvIrwoI0RQMChU
- 0iiDPCGiE3ZCRkvR8VzTeBhnyR5/ccaZNnPhB4t9sdf6Ln5hb8QeathNJFVFKF5C8DqL4ksDVHWGDf7j
- LHyPg6NjviSaFqlu5yQYR+KpupaIkrMknCxT3Y7v/NYYb0ITK1c3BarbWWhLQ7IR0cTKReyZ6lZ0XYei
- ztHpK4bAc+h1FgQijzSxMptrGIxVSO0xX3AaStFki7bUMVFmaMm/eJMGfIH/MkGzLep0AXn4h/r3CJV3
- mS9gn2bY4UY/UzQ7E9TqfeTFtnuB+XAfzSHKr11kSl/uBebDiZ89ZCst3OUkdwL28sIVsE83ock+EIQV
- 2Mz2wxeg6/UAAAAASUVORK5CYII=
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAENSURBVDhPrZHbSgJRGEZ9KX2HfI18I9ELkS5KCREMDBSN
+ VLRA0xDREQ+FTJai47mm8TDOF7NlhulHNwO14LvarHXxb4fjv4jnW6CLZRqIpKoI3Zbw3HwDdX6hC6cI
+ RnNwuT38yE1WoJ6JL5RC+rFxiAji8Uj0rkY9E+9lksnGqMu4TlSoZ7JT9yxihKjLuIoXqcfQNA2yssVo
+ KrOA8+z8eOAiVqAuY7NVMVsqEIdLfiAQyVAX6l7DSt5gIH2hI874AX84TX0o6x2k+Td6HwvUuxI/oD9a
+ 0Q+3+FyjP1qh1ZuiLAztB6yHe+nPUWuP8VB9tx+wHk54naBUH+D+SbQXMP771LgB/dHOqPsnfgDYzPbD
+ 7s5C8AAAAABJRU5ErkJggg==
diff --git a/Analyzer/mainForm.Designer.cs b/Analyzer/mainForm.Designer.cs
index 153c442..5f02e70 100644
--- a/Analyzer/mainForm.Designer.cs
+++ b/Analyzer/mainForm.Designer.cs
@@ -264,6 +264,7 @@
this.toolStripTextBoxVidPid.Size = new System.Drawing.Size(100, 25);
this.toolStripTextBoxVidPid.Text = "0483:0400";
this.toolStripTextBoxVidPid.TextBoxTextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+ this.toolStripTextBoxVidPid.Enter += new System.EventHandler(this.toolStripTextBoxVidPid_Enter);
//
// menuStrip1
//
diff --git a/EonaCat.HID.sln b/EonaCat.HID.sln
index 069fc85..00d167c 100644
--- a/EonaCat.HID.sln
+++ b/EonaCat.HID.sln
@@ -2,10 +2,10 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.34928.147
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EonaCat.HID", "EonaCat.HID\EonaCat.HID.csproj", "{9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EonaCat.HID.Analyzer", "Analyzer\EonaCat.HID.Analyzer.csproj", "{61994020-DB89-4621-BA4B-7347A2142CFF}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EonaCat.HID", "EonaCat.HID\EonaCat.HID.csproj", "{00403BD6-7A26-4971-29D3-8A7849AAC770}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -18,19 +18,6 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}.Debug|x64.ActiveCfg = Debug|Any CPU
- {9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}.Debug|x64.Build.0 = Debug|Any CPU
- {9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}.Debug|x86.ActiveCfg = Debug|Any CPU
- {9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}.Release|Any CPU.Build.0 = Release|Any CPU
- {9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}.Release|x64.ActiveCfg = Release|Any CPU
- {9E8F1D50-74EA-4C60-BD5C-AB2C5B53BC66}.Release|x86.ActiveCfg = Release|Any CPU
{61994020-DB89-4621-BA4B-7347A2142CFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{61994020-DB89-4621-BA4B-7347A2142CFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61994020-DB89-4621-BA4B-7347A2142CFF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -47,6 +34,22 @@ Global
{61994020-DB89-4621-BA4B-7347A2142CFF}.Release|x64.Build.0 = Release|Any CPU
{61994020-DB89-4621-BA4B-7347A2142CFF}.Release|x86.ActiveCfg = Release|Any CPU
{61994020-DB89-4621-BA4B-7347A2142CFF}.Release|x86.Build.0 = Release|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Debug|x64.Build.0 = Debug|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Debug|x86.Build.0 = Debug|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Release|Any CPU.Build.0 = Release|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Release|x64.ActiveCfg = Release|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Release|x64.Build.0 = Release|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Release|x86.ActiveCfg = Release|Any CPU
+ {00403BD6-7A26-4971-29D3-8A7849AAC770}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/EonaCat.HID/Attributes.cs b/EonaCat.HID/Attributes.cs
deleted file mode 100644
index 4f8301b..0000000
--- a/EonaCat.HID/Attributes.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace EonaCat.HID
-{
- public class Attributes
- {
- // This file is part of the EonaCat project(s) which is released under the Apache License.
- // See the LICENSE file or go to https://EonaCat.com/License for full license details.
-
- internal Attributes(NativeMethods.HIDD_ATTRIBUTES attributes)
- {
- VendorId = attributes.VendorID;
- ProductId = attributes.ProductID;
- Version = attributes.VersionNumber;
-
- VendorIdHex = "0x" + attributes.VendorID.ToString("X4");
- ProductIdHex = "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 VendorIdHex { get; set; }
- public string ProductIdHex { get; set; }
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/Capabilities.cs b/EonaCat.HID/Capabilities.cs
deleted file mode 100644
index 00ef07f..0000000
--- a/EonaCat.HID/Capabilities.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-namespace EonaCat.HID
-{
- public class Capabilities
- {
- // This file is part of the EonaCat project(s) which is released under the Apache License.
- // See the LICENSE file or go to https://EonaCat.com/License for full license details.
- internal Capabilities(NativeMethods.Capabilities 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; }
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/Device.cs b/EonaCat.HID/Device.cs
deleted file mode 100644
index 793594e..0000000
--- a/EonaCat.HID/Device.cs
+++ /dev/null
@@ -1,842 +0,0 @@
-using EonaCat.HID.Helpers;
-using System;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace EonaCat.HID
-{
- // This file is part of the EonaCat project(s) which is released under the Apache License.
- // See the LICENSE file or go to https://EonaCat.com/License for full license details.
- public class Device : IDevice
- {
- public event InsertedEventHandler OnInserted;
-
- public event RemovedEventHandler OnRemoved;
-
- private const int BUFFER_SIZE = 254;
- private readonly string _description;
- private readonly string _path;
- private readonly Attributes _attributes;
-
- private readonly Capabilities _capabilities;
- private readonly DeviceEventMonitor _deviceEventMonitor;
-
- private bool _monitorDeviceEvents;
-
- protected delegate DeviceData ReadDelegate(int timeout);
-
- protected delegate Report ReadReportDelegate(int timeout);
-
- private delegate bool WriteDelegate(byte[] data, int timeout);
-
- private delegate bool WriteReportDelegate(Report report, int timeout);
-
- internal Device(string devicePath, string description = null)
- {
- _deviceEventMonitor = new DeviceEventMonitor(this);
- _deviceEventMonitor.Inserted += DeviceEventMonitorInserted;
- _deviceEventMonitor.Removed += DeviceEventMonitorRemoved;
-
- _path = devicePath;
- _description = description;
-
- try
- {
- _deviceModeHelper = new DeviceModeHelper();
-
- var handle = OpenDeviceIO(_path, NativeMethods.ACCESS_NONE);
- _attributes = GetDeviceAttributes(handle);
- _capabilities = GetDeviceCapabilities(handle);
- CloseDeviceIO(handle);
- }
- catch (Exception exception)
- {
- throw new Exception($"EonaCat HID: Error querying device '{devicePath}'.", exception);
- }
- }
-
- public bool IsOpen { get; private set; }
- public bool IsConnected
- { get { return Devices.IsConnected(_path); } }
- public string Description
- { get { return _description; } }
- public Capabilities Capabilities
- { get { return _capabilities; } }
- public Attributes Info
- { get { return _attributes; } }
- public string DevicePath
- { get { return _path; } }
-
- public bool MonitorDeviceEvents
- {
- get { return _monitorDeviceEvents; }
- set
- {
- if (value & !_monitorDeviceEvents)
- {
- _deviceEventMonitor.Init();
- }
-
- _monitorDeviceEvents = value;
- }
- }
-
- private DeviceModeHelper _deviceModeHelper { get; }
-
- public override string ToString()
- {
- return $"VendorID={_attributes.VendorIdHex}, ProductID={_attributes.ProductIdHex}, Version={_attributes.Version}, DevicePath={_path}";
- }
-
- public void OpenDevice()
- {
- OpenDevice(DeviceMode.NonOverlapped, DeviceMode.NonOverlapped, ShareMode.ShareRead | ShareMode.ShareWrite);
- }
-
- public void OpenDevice(DeviceMode readMode, DeviceMode writeMode, ShareMode shareMode)
- {
- if (IsOpen)
- {
- return;
- }
-
- _deviceModeHelper.ReadMode = readMode;
- _deviceModeHelper.WriteMode = writeMode;
- _deviceModeHelper.ShareMode = shareMode;
-
- try
- {
- _deviceModeHelper.ReadHandle = OpenDeviceIO(_path, readMode, NativeMethods.GENERIC_READ, shareMode);
- _deviceModeHelper.WriteHandle = OpenDeviceIO(_path, writeMode, NativeMethods.GENERIC_WRITE, shareMode);
- }
- catch (Exception exception)
- {
- IsOpen = false;
- throw new Exception("EonaCat HID: Error opening device.", exception);
- }
-
- IsOpen = (_deviceModeHelper.ReadHandle.ToInt32() != NativeMethods.INVALID_HANDLE_VALUE && _deviceModeHelper.WriteHandle.ToInt32() != NativeMethods.INVALID_HANDLE_VALUE);
- }
-
- public void CloseDevice()
- {
- if (!IsOpen)
- {
- return;
- }
-
- CloseDeviceIO(_deviceModeHelper.ReadHandle);
- CloseDeviceIO(_deviceModeHelper.WriteHandle);
- IsOpen = false;
- }
-
- public DeviceData Read()
- {
- return Read(0);
- }
-
- public DeviceData Read(int timeout)
- {
- if (IsConnected)
- {
- if (!IsOpen)
- {
- OpenDevice(_deviceModeHelper.ReadMode, _deviceModeHelper.WriteMode, _deviceModeHelper.ShareMode);
- }
-
- try
- {
- return ReadData(timeout);
- }
- catch
- {
- return new DeviceData(DeviceData.ReadStatus.ReadError);
- }
- }
- return new DeviceData(DeviceData.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 StateAsync(readDelegate, callback);
- readDelegate.BeginInvoke(timeout, EndRead, asyncState);
- }
-
- public async Task ReadAsync(int timeout = 0)
- {
- var readDelegate = new ReadDelegate(Read);
-#if NET20 || NET35 || NET5_0_OR_GREATER
- return await Task.Factory.StartNew(() => readDelegate.Invoke(timeout));
-#else
- return await Task.Factory.FromAsync(readDelegate.BeginInvoke, readDelegate.EndInvoke, timeout, null);
-#endif
- }
-
- public Report ReadReport(int timeout = 0)
- {
- return new Report(Capabilities.InputReportByteLength, Read(timeout));
- }
-
- public void ReadReport(ReadReportCallback callback, int timeout = 0)
- {
- var readReportDelegate = new ReadReportDelegate(ReadReport);
- var asyncState = new StateAsync(readReportDelegate, callback);
- readReportDelegate.BeginInvoke(timeout, EndReadReport, asyncState);
- }
-
- public async Task ReadReportAsync(int timeout = 0)
- {
- var readReportDelegate = new ReadReportDelegate(ReadReport);
-#if NET20 || NET35 || NET5_0_OR_GREATER
- return await Task.Factory.StartNew(() => readReportDelegate.Invoke(timeout));
-#else
- return await Task.Factory.FromAsync(readReportDelegate.BeginInvoke, readReportDelegate.EndInvoke, timeout, null);
-#endif
- }
-
- public Report ReadReportSync(byte reportId)
- {
- byte[] commandBuffer = new byte[Capabilities.InputReportByteLength];
- commandBuffer[0] = reportId;
- bool bSuccess = NativeMethods.HidD_GetInputReport(_deviceModeHelper.ReadHandle, commandBuffer, commandBuffer.Length);
- DeviceData deviceData = new DeviceData(commandBuffer, bSuccess ? DeviceData.ReadStatus.Success : DeviceData.ReadStatus.NoDataRead);
- return new Report(Capabilities.InputReportByteLength, deviceData);
- }
-
- public bool ReadFeatureData(out byte[] data, byte reportId = 0)
- {
- if (_capabilities.FeatureReportByteLength <= 0)
- {
- data = new byte[0];
- return false;
- }
-
- data = new byte[_capabilities.FeatureReportByteLength];
-
- var buffer = CreateFeatureOutputBuffer();
- buffer[0] = reportId;
-
- IntPtr hidHandle = IntPtr.Zero;
- bool success = false;
- try
- {
- if (IsOpen)
- {
- hidHandle = _deviceModeHelper.ReadHandle;
- }
- else
- {
- hidHandle = OpenDeviceIO(_path, NativeMethods.ACCESS_NONE);
- }
-
- success = NativeMethods.HidD_GetFeature(hidHandle, buffer, buffer.Length);
-
- if (success)
- {
- Array.Copy(buffer, 0, data, 0, Math.Min(data.Length, _capabilities.FeatureReportByteLength));
- }
- }
- catch (Exception exception)
- {
- throw new Exception(string.Format("EonaCat HID: Error accessing device '{0}'.", _path), exception);
- }
- finally
- {
- if (hidHandle != IntPtr.Zero && hidHandle != _deviceModeHelper.ReadHandle)
- {
- CloseDeviceIO(hidHandle);
- }
- }
-
- return success;
- }
-
- public string ReadProductName()
- {
- if (ReadProductName(out byte[] bytes))
- {
- return ByteHelper.GetFilledBytesString(bytes);
- }
- return string.Empty;
- }
-
- public bool ReadProductName(out byte[] data)
- {
- data = new byte[BUFFER_SIZE];
- IntPtr handle = IntPtr.Zero;
- bool success = false;
- try
- {
- if (IsOpen)
- {
- handle = _deviceModeHelper.ReadHandle;
- }
- else
- {
- handle = OpenDeviceIO(_path, NativeMethods.ACCESS_NONE);
- }
-
- success = NativeMethods.HidD_GetProductString(handle, data, data.Length);
- }
- catch (Exception exception)
- {
- throw new Exception(string.Format("EonaCat HID: Error accessing device '{0}'.", _path), exception);
- }
- finally
- {
- if (handle != IntPtr.Zero && handle != _deviceModeHelper.ReadHandle)
- {
- CloseDeviceIO(handle);
- }
- }
-
- return success;
- }
-
- public string ReadManufacturer()
- {
- if (ReadManufacturer(out byte[] bytes))
- {
- return ByteHelper.GetFilledBytesString(bytes);
- }
- return string.Empty;
- }
-
- public bool ReadManufacturer(out byte[] data)
- {
- data = new byte[BUFFER_SIZE];
- IntPtr handle = IntPtr.Zero;
- bool success = false;
- try
- {
- if (IsOpen)
- {
- handle = _deviceModeHelper.ReadHandle;
- }
- else
- {
- handle = OpenDeviceIO(_path, NativeMethods.ACCESS_NONE);
- }
-
- success = NativeMethods.HidD_GetManufacturerString(handle, data, data.Length);
- }
- catch (Exception exception)
- {
- throw new Exception(string.Format("EonaCat HID: Error accessing device '{0}'.", _path), exception);
- }
- finally
- {
- if (handle != IntPtr.Zero && handle != _deviceModeHelper.ReadHandle)
- {
- CloseDeviceIO(handle);
- }
- }
-
- return success;
- }
-
- public string ReadSerialNumber()
- {
- if (ReadSerialNumber(out byte[] bytes))
- {
- return ByteHelper.GetFilledBytesString(bytes);
- }
- return string.Empty;
- }
-
- public bool ReadSerialNumber(out byte[] data)
- {
- data = new byte[BUFFER_SIZE];
- IntPtr hidHandle = IntPtr.Zero;
- bool success = false;
- try
- {
- if (IsOpen)
- {
- hidHandle = _deviceModeHelper.ReadHandle;
- }
- else
- {
- hidHandle = OpenDeviceIO(_path, NativeMethods.ACCESS_NONE);
- }
-
- success = NativeMethods.HidD_GetSerialNumberString(hidHandle, data, data.Length);
- }
- catch (Exception exception)
- {
- throw new Exception(string.Format("EonaCat HID: Error accessing device '{0}'.", _path), exception);
- }
- finally
- {
- if (hidHandle != IntPtr.Zero && hidHandle != _deviceModeHelper.ReadHandle)
- {
- CloseDeviceIO(hidHandle);
- }
- }
-
- return success;
- }
-
- public bool Write(byte[] data)
- {
- return Write(data, 0);
- }
-
- public bool Write(byte[] data, int timeout)
- {
- if (IsConnected)
- {
- if (!IsOpen)
- {
- OpenDevice(_deviceModeHelper.ReadMode, _deviceModeHelper.WriteMode, _deviceModeHelper.ShareMode);
- }
-
- 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 StateAsync(writeDelegate, callback);
- writeDelegate.BeginInvoke(data, timeout, EndWrite, asyncState);
- }
-
- public async Task WriteAsync(byte[] data, int timeout = 0)
- {
- var writeDelegate = new WriteDelegate(Write);
-#if NET20 || NET35 || NET5_0_OR_GREATER
- return await Task.Factory.StartNew(() => writeDelegate.Invoke(data, timeout));
-#else
- return await Task.Factory.FromAsync(writeDelegate.BeginInvoke, writeDelegate.EndInvoke, data, timeout, null);
-#endif
- }
-
- public bool WriteReport(Report report)
- {
- return WriteReport(report, 0);
- }
-
- public bool WriteReport(Report report, int timeout)
- {
- return Write(report.GetBytes(), timeout);
- }
-
- public void WriteReport(Report report, WriteCallback callback)
- {
- WriteReport(report, callback, 0);
- }
-
- public void WriteReport(Report report, WriteCallback callback, int timeout)
- {
- var writeReportDelegate = new WriteReportDelegate(WriteReport);
- var asyncState = new StateAsync(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(Report report)
- {
- if (null != report)
- {
- byte[] buffer = report.GetBytes();
- return (NativeMethods.HidD_SetOutputReport(_deviceModeHelper.WriteHandle, 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(Report report, int timeout = 0)
- {
- var writeReportDelegate = new WriteReportDelegate(WriteReport);
-#if NET20 || NET35 || NET5_0_OR_GREATER
- return await Task.Factory.StartNew(() => writeReportDelegate.Invoke(report, timeout));
-#else
- return await Task.Factory.FromAsync(writeReportDelegate.BeginInvoke, writeReportDelegate.EndInvoke, report, timeout, null);
-#endif
- }
-
- public Report CreateReport()
- {
- return new Report(Capabilities.OutputReportByteLength);
- }
-
- public bool WriteFeatureData(byte[] data)
- {
- if (_capabilities.FeatureReportByteLength <= 0)
- {
- return false;
- }
-
- var buffer = CreateFeatureOutputBuffer();
-
- Array.Copy(data, 0, buffer, 0, Math.Min(data.Length, _capabilities.FeatureReportByteLength));
-
- IntPtr hidHandle = IntPtr.Zero;
- bool success = false;
- try
- {
- if (IsOpen)
- {
- hidHandle = _deviceModeHelper.WriteHandle;
- }
- else
- {
- hidHandle = OpenDeviceIO(_path, NativeMethods.ACCESS_NONE);
- }
-
- //var overlapped = new NativeOverlapped();
- success = NativeMethods.HidD_SetFeature(hidHandle, buffer, buffer.Length);
- }
- catch (Exception exception)
- {
- throw new Exception(string.Format("EonaCat HID: Error accessing device '{0}'.", _path), exception);
- }
- finally
- {
- if (hidHandle != IntPtr.Zero && hidHandle != _deviceModeHelper.WriteHandle)
- {
- CloseDeviceIO(hidHandle);
- }
- }
- return success;
- }
-
- protected static void EndRead(IAsyncResult ar)
- {
- var hidAsyncState = (StateAsync)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 = (StateAsync)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 = (StateAsync)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 = (StateAsync)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 Attributes GetDeviceAttributes(IntPtr hidHandle)
- {
- var deviceAttributes = default(NativeMethods.HIDD_ATTRIBUTES);
- deviceAttributes.Size = Marshal.SizeOf(deviceAttributes);
- NativeMethods.HidD_GetAttributes(hidHandle, ref deviceAttributes);
- return new Attributes(deviceAttributes);
- }
-
- private static Capabilities GetDeviceCapabilities(IntPtr hidHandle)
- {
- var capabilities = default(NativeMethods.Capabilities);
- var preparsedDataPointer = default(IntPtr);
-
- if (NativeMethods.HidD_GetPreparsedData(hidHandle, ref preparsedDataPointer))
- {
- NativeMethods.HidP_GetCaps(preparsedDataPointer, ref capabilities);
- NativeMethods.HidD_FreePreparsedData(preparsedDataPointer);
- }
- return new Capabilities(capabilities);
- }
-
- private bool WriteData(byte[] data, int timeout)
- {
- if (_capabilities.OutputReportByteLength <= 0)
- {
- return false;
- }
-
- var buffer = CreateOutputBuffer();
- uint bytesWritten;
-
- Array.Copy(data, 0, buffer, 0, Math.Min(data.Length, _capabilities.OutputReportByteLength));
-
- if (_deviceModeHelper.WriteMode == 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(_deviceModeHelper.WriteHandle, 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(_deviceModeHelper.WriteHandle, buffer, (uint)buffer.Length, out bytesWritten, ref overlapped);
- }
- catch { return false; }
- }
- }
-
- protected DeviceData ReadData(int timeout)
- {
- var buffer = new byte[] { };
- var status = DeviceData.ReadStatus.NoDataRead;
- IntPtr nonManagedBuffer;
-
- if (_capabilities.InputReportByteLength > 0)
- {
- uint bytesRead;
-
- buffer = CreateInputBuffer();
- nonManagedBuffer = Marshal.AllocHGlobal(buffer.Length);
-
- if (_deviceModeHelper.ReadMode == 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(_deviceModeHelper.ReadHandle, nonManagedBuffer, (uint)buffer.Length, out bytesRead, ref overlapped);
-
- if (success)
- {
- status = DeviceData.ReadStatus.Success; // No check here to see if bytesRead > 0 . Perhaps not necessary?
- }
- else
- {
- var result = NativeMethods.WaitForSingleObject(overlapped.EventHandle, overlapTimeout);
-
- switch (result)
- {
- case NativeMethods.WAIT_OBJECT_0:
- status = DeviceData.ReadStatus.Success;
- NativeMethods.GetOverlappedResult(_deviceModeHelper.ReadHandle, ref overlapped, out bytesRead, false);
- break;
-
- case NativeMethods.WAIT_TIMEOUT:
- status = DeviceData.ReadStatus.WaitTimedOut;
- buffer = new byte[] { };
- break;
-
- case NativeMethods.WAIT_FAILED:
- status = DeviceData.ReadStatus.WaitFail;
- buffer = new byte[] { };
- break;
-
- default:
- status = DeviceData.ReadStatus.NoDataRead;
- buffer = new byte[] { };
- break;
- }
- }
- Marshal.Copy(nonManagedBuffer, buffer, 0, (int)bytesRead);
- }
- catch { status = DeviceData.ReadStatus.ReadError; }
- finally
- {
- CloseDeviceIO(overlapped.EventHandle);
- Marshal.FreeHGlobal(nonManagedBuffer);
- }
- }
- else
- {
- try
- {
- var overlapped = new NativeOverlapped();
-
- NativeMethods.ReadFile(_deviceModeHelper.ReadHandle, nonManagedBuffer, (uint)buffer.Length, out bytesRead, ref overlapped);
- status = DeviceData.ReadStatus.Success;
- Marshal.Copy(nonManagedBuffer, buffer, 0, (int)bytesRead);
- }
- catch { status = DeviceData.ReadStatus.ReadError; }
- finally { Marshal.FreeHGlobal(nonManagedBuffer); }
- }
- }
- return new DeviceData(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, hTemplateFile: IntPtr.Zero);
- }
-
- 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(_deviceModeHelper.ReadMode, _deviceModeHelper.WriteMode, _deviceModeHelper.ShareMode);
- }
-
- OnInserted?.Invoke();
- }
-
- private void DeviceEventMonitorRemoved()
- {
- if (IsOpen)
- {
- CloseDevice();
- }
-
- OnRemoved?.Invoke();
- }
-
- public void Dispose()
- {
- if (MonitorDeviceEvents)
- {
- MonitorDeviceEvents = false;
- }
-
- if (IsOpen)
- {
- CloseDevice();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/DeviceData.cs b/EonaCat.HID/DeviceData.cs
deleted file mode 100644
index 1904488..0000000
--- a/EonaCat.HID/DeviceData.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-namespace EonaCat.HID
-{
- // This file is part of the EonaCat project(s) which is released under the Apache License.
- // See the LICENSE file or go to https://EonaCat.com/License for full license details.
- public class DeviceData
- {
- public enum ReadStatus
- {
- Success = 0,
- WaitTimedOut = 1,
- WaitFail = 2,
- NoDataRead = 3,
- ReadError = 4,
- NotConnected = 5
- }
-
- public DeviceData(ReadStatus status)
- {
- Data = new byte[] { };
- Status = status;
- }
-
- public DeviceData(byte[] data, ReadStatus status)
- {
- Data = data;
- Status = status;
- }
-
- public byte[] Data { get; private set; }
- public ReadStatus Status { get; private set; }
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/DeviceEventMonitor.cs b/EonaCat.HID/DeviceEventMonitor.cs
deleted file mode 100644
index a22574d..0000000
--- a/EonaCat.HID/DeviceEventMonitor.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System.Threading.Tasks;
-
-namespace EonaCat.HID
-{
- // This file is part of the EonaCat project(s) which is released under the Apache License.
- // See the LICENSE file or go to https://EonaCat.com/License for full license details.
- internal class DeviceEventMonitor
- {
- public event InsertedEventHandler Inserted;
-
- public event RemovedEventHandler Removed;
-
- public delegate void InsertedEventHandler();
-
- public delegate void RemovedEventHandler();
-
- private readonly Device _device;
- private bool _wasConnected;
-
- public DeviceEventMonitor(Device device)
- {
- _device = device;
- }
-
- public void Init()
- {
- Task.Run(EventMonitor);
- }
-
- private async Task EventMonitor()
- {
- while (_device.MonitorDeviceEvents)
- {
- var isConnected = _device.IsConnected;
-
- if (isConnected != _wasConnected)
- {
- if (isConnected)
- {
- Inserted?.Invoke();
- }
- else
- {
- Removed?.Invoke();
- }
-
- _wasConnected = isConnected;
- }
-
- await Task.Delay(100);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/Devices.cs b/EonaCat.HID/Devices.cs
deleted file mode 100644
index 31c52d0..0000000
--- a/EonaCat.HID/Devices.cs
+++ /dev/null
@@ -1,180 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.InteropServices;
-
-namespace EonaCat.HID
-{
- public class Devices
- {
- private static Guid _hidClassGuid = Guid.Empty;
-
- public static bool IsConnected(string devicePath)
- {
- return EnumerateDevices().Any(x => x.Path == devicePath);
- }
-
- public static Device GetDevice(string devicePath)
- {
- return Enumerate(devicePath).FirstOrDefault();
- }
-
- public static IEnumerable Enumerate()
- {
- return EnumerateDevices().Select(x => new Device(x.Path, x.Description));
- }
-
- public static IEnumerable Enumerate(string devicePath)
- {
- return EnumerateDevices().Where(x => x.Path == devicePath).Select(x => new Device(x.Path, x.Description));
- }
-
- public static IEnumerable Enumerate(int vendorId, params int[] productIds)
- {
- return EnumerateDevices().Select(x => new Device(x.Path, x.Description)).Where(x => x.Info.VendorId == vendorId &&
- productIds.Contains(x.Info.ProductId));
- }
-
- public static IEnumerable Enumerate(int vendorId, int productId, ushort UsagePage)
- {
- return EnumerateDevices().Select(x => new Device(x.Path, x.Description)).Where(x => x.Info.VendorId == vendorId &&
- productId == (ushort)x.Info.ProductId && (ushort)x.Capabilities.UsagePage == UsagePage);
- }
-
- public static IEnumerable Enumerate(int vendorId)
- {
- return EnumerateDevices().Select(x => new Device(x.Path, x.Description)).Where(x => x.Info.VendorId == vendorId);
- }
-
- public static IEnumerable Enumerate(ushort UsagePage)
- {
- return EnumerateDevices().Select(x => new Device(x.Path, x.Description)).Where(x => (ushort)x.Capabilities.UsagePage == UsagePage);
- }
-
- 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, hwndParent: IntPtr.Zero, 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.SetupDiGetDeviceInterfaceDetail(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)
- {
- unsafe
- {
- const int charCount = 1024;
- var descriptionBuffer = stackalloc char[charCount];
-
- var requiredSize = 0;
- var type = 0;
-
- if (NativeMethods.SetupDiGetDeviceRegistryProperty(deviceInfoSet,
- ref devinfoData,
- NativeMethods.SPDRP_DEVICEDESC,
- ref type,
- descriptionBuffer,
- propertyBufferSize: charCount * sizeof(char),
- ref requiredSize))
- {
- return new string(descriptionBuffer);
- }
-
- return null;
- }
- }
-
- private static string GetBusReportedDeviceDescription(IntPtr deviceInfoSet, ref NativeMethods.SP_DEVINFO_DATA devinfoData)
- {
- unsafe
- {
- const int charCount = 1024;
- var descriptionBuffer = stackalloc char[charCount];
-
- if (Environment.OSVersion.Version.Major > 5)
- {
- uint propertyType = 0;
- var requiredSize = 0;
-
- if (NativeMethods.SetupDiGetDeviceProperty(deviceInfoSet,
- ref devinfoData,
- ref NativeMethods.DEVPKEY_Device_BusReportedDeviceDesc,
- ref propertyType,
- descriptionBuffer,
- propertyBufferSize: charCount * sizeof(char),
- ref requiredSize,
- 0))
- {
- return new string(descriptionBuffer);
- }
- }
-
- return null;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/Enumerator.cs b/EonaCat.HID/Enumerator.cs
deleted file mode 100644
index cc208f4..0000000
--- a/EonaCat.HID/Enumerator.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-
-namespace EonaCat.HID
-{
- // This file is part of the EonaCat project(s) which is released under the Apache License.
- // See the LICENSE file or go to https://EonaCat.com/License for full license details.
- public class Enumerator : IEnumerator
- {
- public bool IsConnected(string devicePath)
- {
- return Devices.IsConnected(devicePath);
- }
-
- public IDevice GetDevice(string devicePath)
- {
- return Devices.GetDevice(devicePath);
- }
-
- public IEnumerable Enumerate()
- {
- return Devices.Enumerate().Select(x => x as IDevice);
- }
-
- public IEnumerable Enumerate(string devicePath)
- {
- return Devices.Enumerate(devicePath).Select(x => x as IDevice);
- }
-
- public IEnumerable Enumerate(int vendorId, params int[] productIds)
- {
- return Devices.Enumerate(vendorId, productIds).Select(x => x as IDevice);
- }
-
- public IEnumerable Enumerate(int vendorId)
- {
- return Devices.Enumerate(vendorId).Select(x => x as IDevice);
- }
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/EonaCat.HID.csproj b/EonaCat.HID/EonaCat.HID.csproj
index cb7b965..c7e3a77 100644
--- a/EonaCat.HID/EonaCat.HID.csproj
+++ b/EonaCat.HID/EonaCat.HID.csproj
@@ -1,12 +1,13 @@
- net45;netstandard2
+ net46;net6.0
true
EonaCat (Jeroen Saey)
Copyright 2024 EonaCat (Jeroen Saey)
-
+ latest
EonaCat.HID
+ 1.0.3
EonaCat.HID
EonaCat (Jeroen Saey)
HID Devices
@@ -19,17 +20,6 @@
README.md
-
- 1.0.2+{chash:10}.{c:ymd}
- true
- true
- v[0-9]*
- true
- git
- true
- true
-
-
True
@@ -46,10 +36,13 @@
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
+
+
+
+
+ ..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Windows.Forms.dll
+
diff --git a/EonaCat.HID/EventArguments/HidDataReceivedEventArgs.cs b/EonaCat.HID/EventArguments/HidDataReceivedEventArgs.cs
new file mode 100644
index 0000000..41719a5
--- /dev/null
+++ b/EonaCat.HID/EventArguments/HidDataReceivedEventArgs.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace EonaCat.HID.EventArguments
+{
+ ///
+ /// Event args for received data
+ ///
+ public class HidDataReceivedEventArgs : EventArgs
+ {
+ public IHid Device { get; }
+ public byte[] Data { get; }
+
+ public HidDataReceivedEventArgs(IHid device, byte[] data)
+ {
+ Device = device;
+ Data = data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.HID/EventArguments/HidErrorEventArgs.cs b/EonaCat.HID/EventArguments/HidErrorEventArgs.cs
new file mode 100644
index 0000000..97c76ab
--- /dev/null
+++ b/EonaCat.HID/EventArguments/HidErrorEventArgs.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace EonaCat.HID.EventArguments
+{
+ ///
+ /// Event args for error events
+ ///
+ public class HidErrorEventArgs : EventArgs
+ {
+ public IHid Device { get; }
+ public Exception Exception { get; }
+
+ public HidErrorEventArgs(IHid device, Exception ex)
+ {
+ Device = device;
+ Exception = ex;
+ }
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.HID/EventArguments/HidEventArgs.cs b/EonaCat.HID/EventArguments/HidEventArgs.cs
new file mode 100644
index 0000000..bd431de
--- /dev/null
+++ b/EonaCat.HID/EventArguments/HidEventArgs.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace EonaCat.HID.EventArguments
+{
+ ///
+ /// Event arguments with HID device info.
+ ///
+ public class HidEventArgs : EventArgs
+ {
+ public IHid Device { get; }
+
+ public HidEventArgs(IHid device)
+ {
+ Device = device;
+ }
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.HID/Helpers/DeviceModeHelper.cs b/EonaCat.HID/Helpers/DeviceModeHelper.cs
deleted file mode 100644
index ccc2a7c..0000000
--- a/EonaCat.HID/Helpers/DeviceModeHelper.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-
-namespace EonaCat.HID.Helpers
-{
- internal class DeviceModeHelper
- {
- public IntPtr ReadHandle { get; set; }
- public IntPtr WriteHandle { get; set; }
- public DeviceMode ReadMode { get; set; } = DeviceMode.NonOverlapped;
- public DeviceMode WriteMode { get; set; } = DeviceMode.NonOverlapped;
- public ShareMode ShareMode = ShareMode.ShareRead | ShareMode.ShareWrite;
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/HidFactory.cs b/EonaCat.HID/HidFactory.cs
new file mode 100644
index 0000000..e416a7a
--- /dev/null
+++ b/EonaCat.HID/HidFactory.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace EonaCat.HID
+{
+ ///
+ /// Main static factory class to create platform-specific implementations.
+ ///
+ public static class HidFactory
+ {
+ public static string GetPlatform()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return "Windows";
+ }
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return "macOS";
+ }
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return "Linux";
+ }
+
+ return "Unknown";
+ }
+
+ public static bool IsWindows()
+ {
+ return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ }
+
+ public static bool IsLinux()
+ {
+ return RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+ }
+
+ public static bool IsMacOS()
+ {
+ return RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
+ }
+
+ public static IHidManager CreateDeviceManager()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return new Managers.Windows.HidManagerWindows();
+ }
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return new Managers.Linux.HidManagerLinux();
+ }
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return new Managers.Mac.HidManagerMac();
+ }
+ else
+ {
+ throw new PlatformNotSupportedException("Unsupported platform");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.HID/HidLinux.cs b/EonaCat.HID/HidLinux.cs
new file mode 100644
index 0000000..39e9753
--- /dev/null
+++ b/EonaCat.HID/HidLinux.cs
@@ -0,0 +1,392 @@
+using EonaCat.HID.EventArguments;
+using EonaCat.HID.Managers;
+using EonaCat.HID.Managers.Linux;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace EonaCat.HID
+{
+ internal sealed class HidLinux : IHid
+ {
+ private readonly string _devicePath;
+ private FileStream _stream;
+ private readonly object _lock = new object();
+ private CancellationTokenSource _cts;
+
+ public string DevicePath => _devicePath;
+ public ushort VendorId { get; private set; }
+ public ushort ProductId { get; private set; }
+ public string SerialNumber { get; private set; }
+ public string Manufacturer { get; private set; }
+ public string ProductName { get; private set; }
+ public int InputReportByteLength { get; private set; }
+ public int OutputReportByteLength { get; private set; }
+ public int FeatureReportByteLength { get; private set; }
+ public IDictionary Capabilities { get; private set; } = new Dictionary();
+
+ public event EventHandler OnDataReceived;
+ public event EventHandler OnError;
+
+ public HidLinux(string devicePath)
+ {
+ _devicePath = devicePath ?? throw new ArgumentNullException(nameof(devicePath));
+ }
+
+ internal void Setup()
+ {
+ Open();
+ LoadDeviceInfo();
+ LoadReportLengths();
+ Close();
+ }
+
+ public void Open()
+ {
+ lock (_lock)
+ {
+ if (_stream != null)
+ {
+ return;
+ }
+
+ // Open device for read/write
+ var fd = NativeMethods.open(_devicePath, NativeMethods.O_RDWR | NativeMethods.O_NONBLOCK);
+ if (fd < 0)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new IOException($"Failed to open device: {_devicePath}")));
+ return;
+ }
+
+ var safeHandle = new Microsoft.Win32.SafeHandles.SafeFileHandle(new IntPtr(fd), ownsHandle: true);
+ _stream = new FileStream(safeHandle, FileAccess.ReadWrite, bufferSize: 64, isAsync: false);
+ }
+ }
+
+ public void Close()
+ {
+ lock (_lock)
+ {
+ StopListening();
+
+ if (_stream != null)
+ {
+ _stream.Dispose();
+ _stream = null;
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ Close();
+ }
+
+ private void LoadDeviceInfo()
+ {
+ string sysPath = GetSysfsDevicePath(_devicePath);
+ if (string.IsNullOrEmpty(sysPath))
+ {
+ return;
+ }
+
+ VendorId = ReadHexFileAsUShort(Path.Combine(sysPath, "idVendor"));
+ ProductId = ReadHexFileAsUShort(Path.Combine(sysPath, "idProduct"));
+ SerialNumber = ReadTextFile(Path.Combine(sysPath, "serial"));
+ Manufacturer = ReadTextFile(Path.Combine(sysPath, "manufacturer"));
+ ProductName = ReadTextFile(Path.Combine(sysPath, "product"));
+ }
+
+ private void LoadReportLengths()
+ {
+ // As Linux/hidraw interface does not provide direct API for report lengths,
+ // We fallback to standard max USB HID report size or fixed size
+ // TODO: Maybe we should use hidapi or libusb bindings?
+
+ InputReportByteLength = 64; // usually max 64 on USB HID
+ OutputReportByteLength = 64;
+ FeatureReportByteLength = 64;
+ }
+
+ private string GetSysfsDevicePath(string devPath)
+ {
+ // Mapping /dev/hidrawX to sysfs device path: /sys/class/hidraw/hidrawX/device/
+ var devName = Path.GetFileName(devPath);
+ var sysDevicePath = Path.Combine("/sys/class/hidraw", devName, "device");
+ if (Directory.Exists(sysDevicePath))
+ {
+ return sysDevicePath;
+ }
+
+ return null;
+ }
+
+ private ushort ReadHexFileAsUShort(string path)
+ {
+ try
+ {
+ if (File.Exists(path))
+ {
+ string text = File.ReadAllText(path).Trim().ToLowerInvariant();
+ if (text.StartsWith("0x"))
+ {
+ text = text.Substring(2);
+ }
+
+ if (ushort.TryParse(text, System.Globalization.NumberStyles.HexNumber, null, out ushort val))
+ {
+ return val;
+ }
+ }
+ }
+ catch { }
+ return 0;
+ }
+
+ private string ReadTextFile(string path)
+ {
+ try
+ {
+ if (File.Exists(path))
+ {
+ return File.ReadAllText(path).Trim();
+ }
+ }
+ catch { }
+ return null;
+ }
+
+ public async Task WriteOutputReportAsync(byte[] data)
+ {
+ if (data == null)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new ArgumentNullException(nameof(data))));
+ return;
+ }
+
+ if (_stream == null)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new InvalidOperationException("Device not open")));
+ return;
+ }
+
+ await Task.Run(() =>
+ {
+ lock (_lock)
+ {
+ try
+ {
+ _stream.Write(data, 0, data.Length);
+ _stream.Flush();
+ }
+ catch (Exception ex)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, ex));
+ }
+ }
+ });
+ }
+
+ public async Task ReadInputReportAsync()
+ {
+ if (_stream == null)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new InvalidOperationException("Device not open")));
+ return Array.Empty();
+ }
+
+ return await Task.Run(() =>
+ {
+ byte[] buffer = new byte[InputReportByteLength];
+ int bytesRead = 0;
+
+ lock (_lock)
+ {
+ try
+ {
+ bytesRead = _stream.Read(buffer, 0, buffer.Length);
+ }
+ catch (Exception ex)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, ex));
+ }
+ }
+
+ if (bytesRead <= 0)
+ {
+ return Array.Empty();
+ }
+
+ byte[] result = new byte[bytesRead];
+ Array.Copy(buffer, result, bytesRead);
+ return result;
+ });
+ }
+
+ public async Task SendFeatureReportAsync(byte[] data)
+ {
+ if (data == null || data.Length == 0)
+ throw new ArgumentException("Feature report data must not be null or empty.");
+
+ int fd = NativeMethods.open(_devicePath, NativeMethods.O_RDWR);
+ if (fd < 0)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new IOException($"Failed to open device: {_devicePath}")));
+ return;
+ }
+
+ try
+ {
+ int size = data.Length;
+ IntPtr buffer = Marshal.AllocHGlobal(size);
+ Marshal.Copy(data, 0, buffer, size);
+
+ int request = NativeMethods._IOC(NativeMethods._IOC_WRITE, 'H', 0x06, size); // HIDIOCSFEATURE
+ int result = NativeMethods.ioctl(fd, request, buffer);
+ if (result < 0)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new IOException("ioctl HIDIOCSFEATURE failed")));
+ return;
+ }
+
+ Marshal.FreeHGlobal(buffer);
+ }
+ finally
+ {
+ NativeMethods.close(fd);
+ }
+
+ await Task.CompletedTask;
+ }
+
+ public async Task GetFeatureReportAsync(byte reportId)
+ {
+ const int maxFeatureSize = 256;
+ byte[] buffer = new byte[maxFeatureSize];
+ buffer[0] = reportId;
+
+ int fd = NativeMethods.open(_devicePath, NativeMethods.O_RDWR);
+ if (fd < 0)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new IOException($"Failed to open device: {_devicePath}")));
+ return Array.Empty();
+ }
+
+ try
+ {
+ IntPtr bufPtr = Marshal.AllocHGlobal(buffer.Length);
+ Marshal.Copy(buffer, 0, bufPtr, buffer.Length);
+
+ int request = NativeMethods._IOC(NativeMethods._IOC_READ, 'H', 0x07, buffer.Length); // HIDIOCGFEATURE
+ int result = NativeMethods.ioctl(fd, request, bufPtr);
+ if (result < 0)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new IOException("ioctl HIDIOCGFEATURE failed")));
+ return Array.Empty();
+ }
+
+ Marshal.Copy(bufPtr, buffer, 0, buffer.Length);
+ Marshal.FreeHGlobal(bufPtr);
+
+ return await Task.FromResult(buffer);
+ }
+ finally
+ {
+ NativeMethods.close(fd);
+ }
+ }
+
+ public async Task StartListeningAsync(CancellationToken cancellationToken)
+ {
+ if (_stream == null)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new NotSupportedException("Device is not open.")));
+ return;
+ }
+
+ lock (_lock)
+ {
+ if (_cts != null)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new NotSupportedException("Already listening on this device.")));
+ return;
+ }
+
+ _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+ }
+
+ try
+ {
+ var token = _cts.Token;
+
+ while (!token.IsCancellationRequested)
+ {
+ byte[] buffer = new byte[InputReportByteLength];
+ int bytesRead;
+
+ try
+ {
+ bytesRead = await _stream.ReadAsync(buffer, 0, buffer.Length, token);
+ }
+ catch (OperationCanceledException)
+ {
+ break;
+ }
+ catch (NotSupportedException)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new NotSupportedException("Reading input reports is not supported on this device.")));
+ break; // Exit if reading is not supported
+ }
+ catch (Exception ex)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, ex));
+ break;
+ }
+
+ if (bytesRead > 0)
+ {
+ var data = new byte[bytesRead];
+ Array.Copy(buffer, data, bytesRead);
+
+ OnDataReceived?.Invoke(this, new HidDataReceivedEventArgs(this, data));
+ }
+ else
+ {
+ try
+ {
+ await Task.Delay(5, token);
+ }
+ catch (OperationCanceledException)
+ {
+ break;
+ }
+ }
+ }
+ }
+ finally
+ {
+ lock (_lock)
+ {
+ _cts?.Dispose();
+ _cts = null;
+ }
+ }
+ }
+
+ private void StopListening()
+ {
+ lock (_lock)
+ {
+ if (_cts != null)
+ {
+ _cts.Cancel();
+ _cts.Dispose();
+ _cts = null;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.HID/HidMac.cs b/EonaCat.HID/HidMac.cs
new file mode 100644
index 0000000..1e1cf16
--- /dev/null
+++ b/EonaCat.HID/HidMac.cs
@@ -0,0 +1,412 @@
+using EonaCat.HID.EventArguments;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace EonaCat.HID
+{
+ internal sealed class HidMac : IHid
+ {
+ private IntPtr _deviceHandle;
+
+ public HidMac(IntPtr deviceHandle)
+ {
+ _deviceHandle = deviceHandle;
+ CFRetain(_deviceHandle);
+ }
+
+ internal void Setup()
+ {
+ InitializeDeviceInfo();
+ }
+
+ public string DevicePath => null;
+ public ushort VendorId { get; private set; }
+ public ushort ProductId { get; private set; }
+ public string SerialNumber { get; private set; }
+ public string Manufacturer { get; private set; }
+ public string ProductName { get; private set; }
+
+ public int InputReportByteLength { get; private set; } = 64;
+ public int OutputReportByteLength { get; private set; } = 64;
+ public int FeatureReportByteLength { get; private set; } = 64;
+
+ public IDictionary Capabilities { get; private set; } = new Dictionary();
+
+ public event EventHandler OnDataReceived;
+ public event EventHandler OnError;
+
+ public void Open()
+ {
+ IOHIDDeviceOpen(_deviceHandle, 0);
+ }
+
+ public void Close()
+ {
+ IOHIDDeviceClose(_deviceHandle, 0);
+ }
+
+ public void Dispose()
+ {
+ StopListening();
+ Close();
+ CFRelease(_deviceHandle);
+ _deviceHandle = IntPtr.Zero;
+ }
+
+ public void StopListening()
+ {
+ _listeningCts?.Cancel();
+ }
+
+ public async Task WriteOutputReportAsync(byte[] data)
+ {
+ if (data is null)
+ {
+ throw new ArgumentNullException(nameof(data));
+ }
+
+ await Task.Run(() =>
+ {
+ byte reportId = data.Length > 0 ? data[0] : (byte)0;
+ IntPtr buffer = Marshal.AllocHGlobal(data.Length);
+
+ try
+ {
+ Marshal.Copy(data, 0, buffer, data.Length);
+ int res = IOHIDDeviceSetReport(_deviceHandle, 1, reportId, buffer, data.Length);
+ if (res != 0)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new Exception($"IOHIDDeviceSetReport (Output) failed: {res}")));
+ return;
+ }
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(buffer);
+ }
+ });
+ }
+
+ public async Task SendFeatureReportAsync(byte[] data)
+ {
+ if (data is null)
+ {
+ throw new ArgumentNullException(nameof(data));
+ }
+
+ await Task.Run(() =>
+ {
+ byte reportId = data.Length > 0 ? data[0] : (byte)0;
+ IntPtr buffer = Marshal.AllocHGlobal(data.Length);
+
+ try
+ {
+ Marshal.Copy(data, 0, buffer, data.Length);
+ int res = IOHIDDeviceSetReport(_deviceHandle, 2, reportId, buffer, data.Length);
+ if (res != 0)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new Exception($"IOHIDDeviceSetReport (Feature) failed: {res}")));
+ return;
+ }
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(buffer);
+ }
+ });
+ }
+
+ public async Task GetFeatureReportAsync(byte reportId)
+ {
+ return await Task.Run(() =>
+ {
+ int length = FeatureReportByteLength;
+ IntPtr buffer = Marshal.AllocHGlobal(length);
+
+ try
+ {
+ int res = IOHIDDeviceGetReport(_deviceHandle, 2, reportId, buffer, ref length);
+ if (res != 0)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new Exception($"IOHIDDeviceGetReport (Feature) failed: {res}")));
+ return Array.Empty();
+ }
+
+ byte[] outBuf = new byte[length];
+ Marshal.Copy(buffer, outBuf, 0, length);
+ return outBuf;
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(buffer);
+ }
+ });
+ }
+
+ public Task ReadInputReportAsync()
+ {
+ var tcs = new TaskCompletionSource();
+ byte[] buffer = new byte[InputReportByteLength];
+
+ InputReportCallback callback = null;
+
+ callback = (ctx, result, sender, report, reportLength) =>
+ {
+ byte[] output = new byte[reportLength.ToInt32()];
+ Array.Copy(report, output, output.Length);
+ tcs.TrySetResult(output);
+ };
+
+ GCHandle.Alloc(callback);
+ IOHIDDeviceRegisterInputReportCallback(_deviceHandle, buffer, (IntPtr)buffer.Length, callback, IntPtr.Zero);
+
+ return tcs.Task;
+ }
+
+
+ public Task StartListeningAsync(CancellationToken ct)
+ {
+ if (_listeningTask != null && !_listeningTask.IsCompleted)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new NotSupportedException("Already listening on this device.")));
+ return null;
+ }
+
+ _listeningCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
+
+ _listeningTask = Task.Run(() =>
+ {
+ try
+ {
+ byte[] buffer = new byte[InputReportByteLength];
+ InputReportCallback callback = (ctx, result, sender, report, reportLength) =>
+ {
+ byte[] data = new byte[reportLength.ToInt32()];
+ Array.Copy(report, data, data.Length);
+
+ OnDataReceived?.Invoke(this, new HidDataReceivedEventArgs(this, data));
+ };
+
+ GCHandle.Alloc(callback);
+ IOHIDDeviceRegisterInputReportCallback(_deviceHandle, buffer, (IntPtr)buffer.Length, callback, IntPtr.Zero);
+ IOHIDDeviceScheduleWithRunLoop(_deviceHandle, CFRunLoopGetCurrent(), IntPtr.Zero);
+
+ while (!_listeningCts.Token.IsCancellationRequested)
+ {
+ CFRunLoopRun();
+ }
+
+ CFRunLoopStop(CFRunLoopGetCurrent());
+ }
+ catch (OperationCanceledException)
+ {
+ return; // Exit gracefully if cancellation was requested
+ }
+ catch (NotSupportedException)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new NotSupportedException("Reading input reports is not supported on this device.")));
+ _listeningCts.Cancel();
+ }
+ catch (Exception ex)
+ {
+ // Handle exceptions during reading
+ if (_listeningCts.IsCancellationRequested)
+ {
+ // Exit if cancellation was requested
+ }
+ OnError?.Invoke(this, new HidErrorEventArgs(this, ex));
+ }
+ }, _listeningCts.Token);
+
+ return _listeningTask;
+ }
+
+ private void InitializeDeviceInfo()
+ {
+ VendorId = GetIntProperty(kIOHIDVendorIDKey);
+ ProductId = GetIntProperty(kIOHIDProductIDKey);
+ Manufacturer = GetStringProperty(kIOHIDManufacturerKey);
+ ProductName = GetStringProperty(kIOHIDProductKey);
+ SerialNumber = GetStringProperty(kIOHIDSerialNumberKey);
+ }
+
+ private enum CFStringEncoding : uint
+ {
+ UTF8 = 0x08000100,
+ }
+
+ private ushort GetIntProperty(string key)
+ {
+ var cfKey = CFStringCreateWithCString(IntPtr.Zero, key, CFStringEncoding.UTF8);
+ IntPtr val = IOHIDDeviceGetProperty(_deviceHandle, cfKey);
+ CFRelease(cfKey);
+
+ if (val == IntPtr.Zero)
+ {
+ return 0;
+ }
+
+ if (CFGetTypeID(val) == CFNumberGetTypeID())
+ {
+ int i = 0;
+ bool success = CFNumberGetValue(val, CFNumberType.kCFNumberIntType, out i);
+ if (success)
+ {
+ return (ushort)i;
+ }
+ }
+
+ return 0;
+ }
+
+ private string GetStringProperty(string key)
+ {
+ var cfKey = CFStringCreateWithCString(IntPtr.Zero, key, CFStringEncoding.UTF8);
+ IntPtr val = IOHIDDeviceGetProperty(_deviceHandle, cfKey);
+ CFRelease(cfKey);
+ if (val == IntPtr.Zero)
+ {
+ return null;
+ }
+
+ string s = CFStringToString(val);
+ CFRelease(val);
+ return s;
+ }
+
+ private string CFStringToString(IntPtr cfStr)
+ {
+ if (cfStr == IntPtr.Zero)
+ {
+ return null;
+ }
+
+ IntPtr ptr = CFStringGetCStringPtr(cfStr, CFStringEncoding.UTF8);
+ if (ptr != IntPtr.Zero)
+ {
+ // Manually read null-terminated UTF8 string from pointer
+ return PtrToStringUTF8Manual(ptr);
+ }
+
+ int len = CFStringGetLength(cfStr);
+ int max = CFStringGetMaximumSizeForEncoding(len, CFStringEncoding.UTF8) + 1;
+ byte[] buf = new byte[max];
+
+ bool success = CFStringGetCString(cfStr, buf, max, CFStringEncoding.UTF8);
+ if (success)
+ {
+ return System.Text.Encoding.UTF8.GetString(buf).TrimEnd('\0');
+ }
+
+ return null;
+ }
+
+ private string PtrToStringUTF8Manual(IntPtr ptr)
+ {
+ if (ptr == IntPtr.Zero)
+ {
+ return null;
+ }
+
+ // Find length of null-terminated string
+ int len = 0;
+ while (Marshal.ReadByte(ptr, len) != 0)
+ {
+ len++;
+ }
+
+ if (len == 0)
+ {
+ return string.Empty;
+ }
+
+ byte[] buffer = new byte[len];
+ Marshal.Copy(ptr, buffer, 0, len);
+ return System.Text.Encoding.UTF8.GetString(buffer);
+ }
+
+ // Constants for IOHIDDevice properties
+ private const string kIOHIDVendorIDKey = "VendorID";
+ private const string kIOHIDProductIDKey = "ProductID";
+ private const string kIOHIDManufacturerKey = "Manufacturer";
+ private const string kIOHIDProductKey = "Product";
+ private const string kIOHIDSerialNumberKey = "SerialNumber";
+
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ static extern IntPtr IOHIDDeviceGetProperty(IntPtr dev, IntPtr key);
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ static extern uint IOHIDDeviceOpen(IntPtr dev, uint opt);
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ static extern uint IOHIDDeviceClose(IntPtr dev, uint opt);
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ static extern int IOHIDDeviceSetReport(IntPtr dev, int type, byte id, IntPtr buf, int len);
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ static extern int IOHIDDeviceGetReport(IntPtr dev, int type, byte id, IntPtr buf, ref int len);
+
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ static extern IntPtr CFStringCreateWithCString(IntPtr alloc, string s, CFStringEncoding enc);
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ static extern void CFRelease(IntPtr cf);
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ static extern IntPtr CFRetain(IntPtr cf);
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ static extern IntPtr CFStringGetCStringPtr(IntPtr str, CFStringEncoding enc);
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ static extern int CFStringGetLength(IntPtr str);
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ static extern int CFStringGetMaximumSizeForEncoding(int len, CFStringEncoding enc);
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ static extern bool CFStringGetCString(IntPtr str, byte[] buf, int bufSize, CFStringEncoding enc);
+
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ static extern IntPtr CFGetTypeID(IntPtr cf);
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ static extern IntPtr CFNumberGetTypeID();
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ static extern bool CFNumberGetValue(IntPtr num, CFNumberType type, out int value);
+
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ private static extern void IOHIDDeviceRegisterInputReportCallback(
+ IntPtr device,
+ byte[] report,
+ IntPtr reportLength,
+ InputReportCallback callback,
+ IntPtr context);
+
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ private static extern void IOHIDDeviceScheduleWithRunLoop(
+ IntPtr device,
+ IntPtr runLoop,
+ IntPtr runLoopMode);
+
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ private static extern IntPtr CFRunLoopGetCurrent();
+
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ private static extern void CFRunLoopRun();
+
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ private static extern void CFRunLoopStop(IntPtr runLoop);
+
+ // Input report callback delegate
+ private delegate void InputReportCallback(
+ IntPtr context,
+ IntPtr result,
+ IntPtr sender,
+ byte[] report,
+ IntPtr reportLength);
+
+ // For managing lifetime of listening loop
+ private CancellationTokenSource _listeningCts;
+ private Task _listeningTask;
+
+
+ private enum CFNumberType : int
+ {
+ kCFNumberIntType = 9
+ }
+ }
+}
diff --git a/EonaCat.HID/HidWindows.cs b/EonaCat.HID/HidWindows.cs
new file mode 100644
index 0000000..5043638
--- /dev/null
+++ b/EonaCat.HID/HidWindows.cs
@@ -0,0 +1,461 @@
+using EonaCat.HID.EventArguments;
+using EonaCat.HID.Managers.Windows;
+using Microsoft.Win32.SafeHandles;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using static EonaCat.HID.Managers.Windows.NativeMethods;
+
+namespace EonaCat.HID
+{
+ internal sealed class HidWindows : IHid
+ {
+ private SafeFileHandle _deviceHandle;
+ private FileStream _deviceStream;
+ private IntPtr _preparsedData;
+
+ private bool _isOpen;
+ private readonly string _devicePath;
+
+ public string DevicePath => _devicePath;
+ public ushort VendorId { get; private set; }
+ public ushort ProductId { get; private set; }
+ public string SerialNumber { get; private set; }
+ public string Manufacturer { get; private set; }
+ public string ProductName { get; private set; }
+ public int InputReportByteLength { get; private set; }
+ public int OutputReportByteLength { get; private set; }
+ public int FeatureReportByteLength { get; private set; }
+
+ public IDictionary Capabilities { get; } = new Dictionary();
+
+ public event EventHandler OnDataReceived;
+ public event EventHandler OnError;
+
+ private CancellationTokenSource _listeningCts;
+
+ private readonly object _lockObject = new object();
+
+ public HidWindows(string devicePath)
+ {
+ _devicePath = devicePath ?? throw new ArgumentNullException(nameof(devicePath));
+ }
+
+ internal void Setup()
+ {
+ Open();
+ LoadDeviceAttributes();
+ Close();
+ }
+
+ private void LoadDeviceAttributes()
+ {
+ Manufacturer = GetStringDescriptor(HidD_GetManufacturerString);
+ ProductName = GetStringDescriptor(HidD_GetProductString);
+ SerialNumber = GetStringDescriptor(HidD_GetSerialNumberString);
+ HidDeviceAttributes attr = GetDeviceAttributes();
+ VendorId = attr.VendorID;
+ ProductId = attr.ProductID;
+ }
+
+ ///
+ /// Open the device for I/O
+ ///
+ public void Open()
+ {
+ if (_isOpen)
+ return;
+
+ FileAccess access = FileAccess.ReadWrite;
+ SafeFileHandle handle = TryOpenDevice(GENERIC_READ | GENERIC_WRITE);
+
+ if (handle == null || handle.IsInvalid)
+ {
+ handle = TryOpenDevice(GENERIC_READ);
+ if (handle != null && !handle.IsInvalid)
+ access = FileAccess.Read;
+ }
+
+ if ((handle == null || handle.IsInvalid) && Environment.Is64BitOperatingSystem)
+ {
+ handle = TryOpenDevice(GENERIC_WRITE);
+ if (handle != null && !handle.IsInvalid)
+ access = FileAccess.Write;
+ }
+
+ if (handle == null || handle.IsInvalid)
+ {
+ int err = Marshal.GetLastWin32Error();
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new Win32Exception(err, $"Cannot open device {_devicePath} with any access mode")));
+ return;
+ }
+
+ _deviceHandle = handle;
+ _deviceStream = new FileStream(_deviceHandle, access, 64, true);
+ _isOpen = true;
+
+ // HID descriptor parsing
+ if (!HidD_GetPreparsedData(_deviceHandle.DangerousGetHandle(), out _preparsedData))
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed HidD_GetPreparsedData");
+
+ HIDP_CAPS caps;
+ int res = HidP_GetCaps(_preparsedData, out caps);
+ if (res == 0)
+ throw new Win32Exception(res, "Failed HidP_GetCaps");
+
+ InputReportByteLength = caps.InputReportByteLength;
+ OutputReportByteLength = caps.OutputReportByteLength;
+ FeatureReportByteLength = caps.FeatureReportByteLength;
+
+ Capabilities["Usage"] = caps.Usage;
+ Capabilities["UsagePage"] = caps.UsagePage;
+ Capabilities["InputReportByteLength"] = InputReportByteLength;
+ Capabilities["OutputReportByteLength"] = OutputReportByteLength;
+ Capabilities["FeatureReportByteLength"] = FeatureReportByteLength;
+
+ Manufacturer = GetStringDescriptor(HidD_GetManufacturerString);
+ ProductName = GetStringDescriptor(HidD_GetProductString);
+ SerialNumber = GetStringDescriptor(HidD_GetSerialNumberString);
+
+ HidDeviceAttributes attr = GetDeviceAttributes();
+ VendorId = attr.VendorID;
+ ProductId = attr.ProductID;
+ }
+
+
+ private SafeFileHandle TryOpenDevice(int access)
+ {
+ var handle = CreateFile(_devicePath,
+ access,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ IntPtr.Zero,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ IntPtr.Zero);
+
+ return handle;
+ }
+
+ private bool CanRead(SafeFileHandle handle)
+ {
+ try
+ {
+ using (var stream = new FileStream(handle, FileAccess.Read, 1, true))
+ {
+ byte[] test = new byte[1];
+ stream.Read(test, 0, 1);
+ return true;
+ }
+ }
+ catch { return false; }
+ }
+
+ private bool CanWrite(SafeFileHandle handle)
+ {
+ try
+ {
+ using (var stream = new FileStream(handle, FileAccess.Write, 1, true))
+ {
+ byte[] test = new byte[1];
+ stream.Write(test, 0, 1);
+ return true;
+ }
+ }
+ catch { return false; }
+ }
+
+ ///
+ /// Close the device
+ ///
+ public void Close()
+ {
+ lock (_lockObject)
+ {
+ if (!_isOpen)
+ {
+ return;
+ }
+
+ StopListening();
+
+ if (_preparsedData != IntPtr.Zero)
+ {
+ HidD_FreePreparsedData(_preparsedData);
+ _preparsedData = IntPtr.Zero;
+ }
+
+ _deviceStream?.Dispose();
+ _deviceStream = null;
+
+ _deviceHandle?.Dispose();
+ _deviceHandle = null;
+
+ _isOpen = false;
+ }
+ }
+ private HidDeviceAttributes GetDeviceAttributes()
+ {
+ HidDeviceAttributes attr = new HidDeviceAttributes
+ {
+ Size = Marshal.SizeOf()
+ };
+ if (!HidD_GetAttributes(_deviceHandle.DangerousGetHandle(), ref attr))
+ {
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed HidD_GetAttributes");
+ }
+ return attr;
+ }
+
+ private string GetStringDescriptor(Func stringFunc)
+ {
+ var buffer = new byte[126 * 2]; // Unicode max buffer
+ GCHandle gc = GCHandle.Alloc(buffer, GCHandleType.Pinned);
+ try
+ {
+ bool success = stringFunc(_deviceHandle.DangerousGetHandle(), gc.AddrOfPinnedObject(), buffer.Length);
+ if (!success)
+ {
+ return null;
+ }
+
+ string str = Encoding.Unicode.GetString(buffer);
+ int idx = str.IndexOf('\0');
+ if (idx >= 0)
+ {
+ str = str.Substring(0, idx);
+ }
+
+ return str;
+ }
+ finally
+ {
+ gc.Free();
+ }
+ }
+
+ public void Dispose()
+ {
+ Close();
+ }
+
+ public async Task WriteOutputReportAsync(byte[] data)
+ {
+ if (!_isOpen)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new InvalidOperationException("Device not open")));
+ return;
+ }
+
+ if (data == null || data.Length == 0)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new ArgumentNullException(nameof(data), "Data cannot be null or empty")));
+ return;
+ }
+
+ try
+ {
+ await _deviceStream.WriteAsync(data, 0, data.Length);
+ await _deviceStream.FlushAsync();
+ }
+ catch (Exception ex)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, ex));
+ throw;
+ }
+ }
+
+ public async Task ReadInputReportAsync()
+ {
+ if (!_isOpen)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new InvalidOperationException("Device not open")));
+ return Array.Empty();
+ }
+
+ return await Task.Run(async () =>
+ {
+ var buffer = new byte[InputReportByteLength];
+
+ try
+ {
+ int read = await _deviceStream.ReadAsync(buffer, 0, buffer.Length);
+ if (read == 0)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new IOException("No data read from device")));
+ return Array.Empty();
+ }
+
+ return buffer.Take(read).ToArray();
+ }
+ catch (Exception ex)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, ex));
+ throw;
+ }
+ });
+ }
+
+ public async Task SendFeatureReportAsync(byte[] data)
+ {
+ if (!_isOpen)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new InvalidOperationException("Device not open")));
+ return;
+ }
+
+ if (data == null || data.Length == 0)
+ {
+ throw new ArgumentNullException(nameof(data));
+ }
+
+ await Task.Run(() =>
+ {
+ bool success = HidD_SetFeature(_deviceHandle.DangerousGetHandle(), data, data.Length);
+ if (!success)
+ {
+ var err = Marshal.GetLastWin32Error();
+ var ex = new Win32Exception(err, "HidD_SetFeature failed");
+ OnError?.Invoke(this, new HidErrorEventArgs(this, ex));
+ throw ex;
+ }
+ });
+ }
+
+ public async Task GetFeatureReportAsync(byte reportId)
+ {
+ if (!_isOpen)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new InvalidOperationException("Device not open")));
+ return Array.Empty();
+ }
+
+ return await Task.Run(() =>
+ {
+ var buffer = new byte[FeatureReportByteLength];
+ buffer[0] = reportId;
+
+ bool success = HidD_GetFeature(_deviceHandle.DangerousGetHandle(), buffer, buffer.Length);
+ if (!success)
+ {
+ var err = Marshal.GetLastWin32Error();
+ var ex = new Win32Exception(err, "HidD_GetFeature failed");
+ OnError?.Invoke(this, new HidErrorEventArgs(this, ex));
+ return Array.Empty();
+ }
+
+ return buffer;
+ });
+ }
+
+ ///
+ /// Begin async reading loop raising OnDataReceived events on data input
+ ///
+ ///
+ ///
+ public async Task StartListeningAsync(CancellationToken cancellationToken)
+ {
+ if (!_isOpen)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new NotSupportedException("Device is not open.")));
+ return;
+ }
+
+ if (_listeningCts != null)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new NotSupportedException("Already listening on this device.")));
+ return;
+ }
+
+ _listeningCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+
+ try
+ {
+ while (!_listeningCts.Token.IsCancellationRequested)
+ {
+ try
+ {
+ byte[] data = await ReadInputReportAsync(_listeningCts.Token);
+
+ if (data != null && data.Length > 0)
+ {
+ OnDataReceived?.Invoke(this, new HidDataReceivedEventArgs(this, data));
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ break;
+ }
+ catch (NotSupportedException)
+ {
+ OnError?.Invoke(this, new HidErrorEventArgs(this, new NotSupportedException("Reading input reports is not supported on this device.")));
+ break; // Exit if reading is not supported
+ }
+ catch (Exception ex)
+ {
+ // Handle exceptions during reading
+ if (_listeningCts.IsCancellationRequested)
+ {
+ break; // Exit if cancellation was requested
+ }
+ OnError?.Invoke(this, new HidErrorEventArgs(this, ex));
+ }
+ }
+ }
+ finally
+ {
+ _listeningCts.Dispose();
+ _listeningCts = null;
+ }
+ }
+
+ private Task ReadInputReportAsync(CancellationToken cancellationToken)
+ {
+ var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ // Use overlapped IO pattern from FileStream
+ var buffer = new byte[InputReportByteLength];
+ _deviceStream.BeginRead(buffer, 0, buffer.Length, ar =>
+ {
+ try
+ {
+ int bytesRead = _deviceStream.EndRead(ar);
+ if (bytesRead == 0)
+ {
+ tcs.SetResult(Array.Empty());
+ }
+ else
+ {
+ tcs.SetResult(buffer.Take(bytesRead).ToArray());
+ }
+ }
+ catch (Exception ex)
+ {
+ tcs.SetException(ex);
+ }
+ }, null);
+
+ cancellationToken.Register(() =>
+ {
+ tcs.TrySetCanceled();
+ });
+
+ return tcs.Task;
+ }
+
+ private void StopListening()
+ {
+ if (_listeningCts != null)
+ {
+ _listeningCts.Cancel();
+ _listeningCts.Dispose();
+ _listeningCts = null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.HID/IDevice.cs b/EonaCat.HID/IDevice.cs
deleted file mode 100644
index 50c3633..0000000
--- a/EonaCat.HID/IDevice.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-using System;
-using System.Threading.Tasks;
-
-namespace EonaCat.HID
-{
- // This file is part of the EonaCat project(s) which is released under the Apache License.
- // See the LICENSE file or go to https://EonaCat.com/License for full license details.
-
- 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(DeviceData data);
-
- public delegate void ReadReportCallback(Report report);
-
- public delegate void WriteCallback(bool success);
-
- public interface IDevice : IDisposable
- {
- event InsertedEventHandler OnInserted;
-
- event RemovedEventHandler OnRemoved;
-
- bool IsOpen { get; }
- bool IsConnected { get; }
- string Description { get; }
- Capabilities Capabilities { get; }
- Attributes Info { get; }
- string DevicePath { get; }
-
- bool MonitorDeviceEvents { get; set; }
-
- void OpenDevice();
-
- void OpenDevice(DeviceMode readMode, DeviceMode writeMode, ShareMode shareMode);
-
- void CloseDevice();
-
- DeviceData Read();
-
- void Read(ReadCallback callback, int timeout = 0);
-
- Task ReadAsync(int timeout = 0);
-
- DeviceData Read(int timeout);
-
- void ReadReport(ReadReportCallback callback, int timeout = 0);
-
- Task ReadReportAsync(int timeout = 0);
-
- Report ReadReport(int timeout = 0);
-
- bool ReadFeatureData(out byte[] data, byte reportId = 0);
-
- bool ReadProductName(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(Report report, WriteCallback callback);
-
- bool WriteReport(Report report);
-
- bool WriteReport(Report report, int timeout);
-
- void WriteReport(Report report, WriteCallback callback, int timeout);
-
- Task WriteReportAsync(Report report, int timeout = 0);
-
- Report CreateReport();
-
- bool WriteFeatureData(byte[] data);
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/IEnumerator.cs b/EonaCat.HID/IEnumerator.cs
deleted file mode 100644
index 1d0ed36..0000000
--- a/EonaCat.HID/IEnumerator.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System.Collections.Generic;
-
-namespace EonaCat.HID
-{
- // This file is part of the EonaCat project(s) which is released under the Apache License.
- // See the LICENSE file or go to https://EonaCat.com/License for full license details.
- public interface IEnumerator
- {
- bool IsConnected(string devicePath);
-
- IDevice GetDevice(string devicePath);
-
- IEnumerable Enumerate();
-
- IEnumerable Enumerate(string devicePath);
-
- IEnumerable Enumerate(int vendorId, params int[] productIds);
-
- IEnumerable Enumerate(int vendorId);
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/IHid.cs b/EonaCat.HID/IHid.cs
new file mode 100644
index 0000000..88ed556
--- /dev/null
+++ b/EonaCat.HID/IHid.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Threading;
+using EonaCat.HID.EventArguments;
+
+namespace EonaCat.HID
+{
+ ///
+ /// Interface abstraction for a HID device.
+ ///
+ public interface IHid : IDisposable
+ {
+ string DevicePath { get; }
+ ushort VendorId { get; }
+ ushort ProductId { get; }
+ string SerialNumber { get; }
+ string Manufacturer { get; }
+ string ProductName { get; }
+ int InputReportByteLength { get; }
+ int OutputReportByteLength { get; }
+ int FeatureReportByteLength { get; }
+
+ IDictionary Capabilities { get; }
+
+ ///
+ /// Opens the device for communication
+ ///
+ void Open();
+
+ ///
+ /// Closes the device
+ ///
+ void Close();
+
+ ///
+ /// Writes an output report to the device
+ ///
+ /// Complete report data including ReportID
+ Task WriteOutputReportAsync(byte[] data);
+
+ ///
+ /// Reads an input report
+ ///
+ /// Input report data
+ Task ReadInputReportAsync();
+
+ ///
+ /// Sends a feature report
+ ///
+ /// Complete feature report data including ReportID
+ Task SendFeatureReportAsync(byte[] data);
+
+ ///
+ /// Gets a feature report
+ ///
+ /// Feature report data
+ Task GetFeatureReportAsync(byte reportId);
+
+ ///
+ /// Asynchronously read input reports and raise OnDataReceived event
+ ///
+ ///
+ Task StartListeningAsync(CancellationToken cancellationToken);
+
+ ///
+ /// Occurs when data is received from the device asynchronously
+ ///
+ event EventHandler OnDataReceived;
+
+ ///
+ /// Errors occurring on device operations
+ ///
+ event EventHandler OnError;
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.HID/IHidManager.cs b/EonaCat.HID/IHidManager.cs
new file mode 100644
index 0000000..4789e52
--- /dev/null
+++ b/EonaCat.HID/IHidManager.cs
@@ -0,0 +1,27 @@
+using EonaCat.HID.EventArguments;
+using System;
+using System.Collections.Generic;
+
+namespace EonaCat.HID
+{
+ ///
+ /// Interface for manager to enumerate devices and monitor connect/disconnect
+ ///
+ public interface IHidManager
+ {
+ ///
+ /// Enumerate all connected HID devices matching optional VendorId/ProductId filters
+ ///
+ IEnumerable Enumerate(ushort? vendorId = null, ushort? productId = null);
+
+ ///
+ /// Event is raised when a HID device is inserted
+ ///
+ event EventHandler OnDeviceInserted;
+
+ ///
+ /// Event is raised when a HID device is removed
+ ///
+ event EventHandler OnDeviceRemoved;
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.HID/Managers/HidManagerLinux.cs b/EonaCat.HID/Managers/HidManagerLinux.cs
new file mode 100644
index 0000000..ca088ed
--- /dev/null
+++ b/EonaCat.HID/Managers/HidManagerLinux.cs
@@ -0,0 +1,176 @@
+using EonaCat.HID.EventArguments;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using static EonaCat.HID.Managers.Linux.NativeMethods;
+
+namespace EonaCat.HID.Managers.Linux
+{
+ internal sealed class HidManagerLinux : IHidManager
+ {
+ public event EventHandler OnDeviceInserted;
+ public event EventHandler OnDeviceRemoved;
+ public event EventHandler OnDeviceError;
+
+ public IEnumerable Enumerate(ushort? vendorId = null, ushort? productId = null)
+ {
+ var hidrawDir = "/sys/class/hidraw/";
+
+ if (!Directory.Exists(hidrawDir))
+ {
+ yield break;
+ }
+
+ var hidrawEntries = Directory.GetDirectories(hidrawDir).Where(d => d.Contains("hidraw"));
+
+ foreach (var hidrawEntry in hidrawEntries)
+ {
+ var devName = Path.GetFileName(hidrawEntry);
+
+ var devPath = "/dev/" + devName;
+ if (!File.Exists(devPath))
+ {
+ continue;
+ }
+
+ var dev = new HidLinux(devPath);
+ dev.Setup();
+
+ if (vendorId.HasValue && dev.VendorId != vendorId)
+ {
+ continue;
+ }
+
+ if (productId.HasValue && dev.ProductId != productId)
+ {
+ continue;
+ }
+
+ yield return dev;
+ }
+ }
+
+ private void DeviceInserted(IHid device)
+ {
+ OnDeviceInserted?.Invoke(this, new HidEventArgs(device));
+ }
+
+ private void DeviceRemoved(IHid device)
+ {
+ OnDeviceRemoved?.Invoke(this, new HidEventArgs(device));
+ }
+
+ public HidManagerLinux()
+ {
+ Task.Run(() => MonitorDevices());
+ }
+
+ private void MonitorDevices()
+ {
+ var previousDevices = new Dictionary();
+
+ const int maxErrors = 10;
+ TimeSpan errorWindow = TimeSpan.FromMinutes(5);
+ Queue errorTimestamps = new Queue();
+
+ while (true)
+ {
+ try
+ {
+ var currentDevices = Enumerate().ToList();
+ var currentDeviceDict = currentDevices.ToDictionary(d => d.DevicePath);
+
+ // Detect new devices
+ foreach (var kvp in currentDeviceDict)
+ {
+ if (!previousDevices.ContainsKey(kvp.Key))
+ {
+ DeviceInserted(kvp.Value);
+ }
+ }
+
+ // Detect removed devices
+ foreach (var kvp in previousDevices)
+ {
+ if (!currentDeviceDict.ContainsKey(kvp.Key))
+ {
+ DeviceRemoved(kvp.Value);
+ }
+ }
+
+ previousDevices = currentDeviceDict;
+
+ // Clear error log on success
+ errorTimestamps.Clear();
+ }
+ catch (Exception ex)
+ {
+ OnDeviceError?.Invoke(this, $"[MonitorDevices] Error: {ex.Message}");
+
+ var now = DateTime.UtcNow;
+ errorTimestamps.Enqueue(now);
+
+ // Remove timestamps outside the 5-minute window
+ while (errorTimestamps.Count > 0 && now - errorTimestamps.Peek() > errorWindow)
+ {
+ errorTimestamps.Dequeue();
+ }
+
+ if (errorTimestamps.Count >= maxErrors)
+ {
+ Console.Error.WriteLine($"[MonitorDevices] Too many errors ({errorTimestamps.Count}) in the last 5 minutes. Monitoring stopped.");
+ break;
+ }
+ }
+ Thread.Sleep(1000); // Poll every second
+ }
+ }
+ }
+
+ internal static class NativeMethods
+ {
+ public const int O_RDWR = 0x0002;
+ public const int O_NONBLOCK = 0x800;
+
+ [DllImport("libc", SetLastError = true)]
+ public static extern int open(string pathname, int flags);
+
+ [DllImport("libc", SetLastError = true)]
+ public static extern int close(int fd);
+
+ [DllImport("libc", SetLastError = true)]
+ public static extern int ioctl(int fd, int request, IntPtr data);
+
+ // _IOC macro emulation
+ public const int _IOC_NRBITS = 8;
+ public const int _IOC_TYPEBITS = 8;
+ public const int _IOC_SIZEBITS = 14;
+ public const int _IOC_DIRBITS = 2;
+
+ public const int _IOC_NRMASK = (1 << _IOC_NRBITS) - 1;
+ public const int _IOC_TYPEMASK = (1 << _IOC_TYPEBITS) - 1;
+ public const int _IOC_SIZEMASK = (1 << _IOC_SIZEBITS) - 1;
+ public const int _IOC_DIRMASK = (1 << _IOC_DIRBITS) - 1;
+
+ public const int _IOC_NRSHIFT = 0;
+ public const int _IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS;
+ public const int _IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS;
+ public const int _IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS;
+
+ public const int _IOC_NONE = 0;
+ public const int _IOC_WRITE = 1;
+ public const int _IOC_READ = 2;
+
+ public static int _IOC(int dir, int type, int nr, int size)
+ {
+ return ((dir & _IOC_DIRMASK) << _IOC_DIRSHIFT) |
+ ((type & _IOC_TYPEMASK) << _IOC_TYPESHIFT) |
+ ((nr & _IOC_NRMASK) << _IOC_NRSHIFT) |
+ ((size & _IOC_SIZEMASK) << _IOC_SIZESHIFT);
+ }
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.HID/Managers/HidManagerMac.cs b/EonaCat.HID/Managers/HidManagerMac.cs
new file mode 100644
index 0000000..3bc65ed
--- /dev/null
+++ b/EonaCat.HID/Managers/HidManagerMac.cs
@@ -0,0 +1,156 @@
+using EonaCat.HID.EventArguments;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using static EonaCat.HID.Managers.Mac.NativeMethods;
+
+namespace EonaCat.HID.Managers.Mac
+{
+ internal sealed class HidManagerMac : IHidManager, IDisposable
+ {
+ private IntPtr _hidManager;
+ private readonly IOHIDDeviceCallback _deviceAddedCallback;
+ private readonly IOHIDDeviceCallback _deviceRemovedCallback;
+
+ public event EventHandler OnDeviceInserted;
+ public event EventHandler OnDeviceRemoved;
+
+ public HidManagerMac()
+ {
+ _deviceAddedCallback = DeviceAddedCallback;
+ _deviceRemovedCallback = DeviceRemovedCallback;
+
+ _hidManager = IOHIDManagerCreate(IntPtr.Zero, 0);
+ if (_hidManager == IntPtr.Zero)
+ {
+ throw new InvalidOperationException("Failed to create IOHIDManager");
+ }
+
+ CFRetain(_hidManager);
+
+ IOHIDManagerScheduleWithRunLoop(
+ _hidManager,
+ CFRunLoopGetCurrent(),
+ CFRunLoopModeDefault
+ );
+
+ if (IOHIDManagerOpen(_hidManager, 0) != 0)
+ {
+ throw new InvalidOperationException("Failed to open IOHIDManager");
+ }
+
+ IOHIDManagerRegisterDeviceMatchingCallback(_hidManager, _deviceAddedCallback, IntPtr.Zero);
+ IOHIDManagerRegisterDeviceRemovalCallback(_hidManager, _deviceRemovedCallback, IntPtr.Zero);
+ }
+
+ public IEnumerable Enumerate(ushort? vendorId = null, ushort? productId = null)
+ {
+ var devices = new List();
+ IntPtr cfSet = IOHIDManagerCopyDevices(_hidManager);
+ if (cfSet == IntPtr.Zero)
+ {
+ return devices;
+ }
+
+ int count = CFSetGetCount(cfSet);
+ IntPtr[] values = new IntPtr[count];
+ CFSetGetValues(cfSet, values);
+ foreach (var ptr in values)
+ {
+ var dev = new HidMac(ptr);
+ dev.Setup();
+ if (vendorId.HasValue && dev.VendorId != vendorId)
+ {
+ continue;
+ }
+
+ if (productId.HasValue && dev.ProductId != productId)
+ {
+ continue;
+ }
+
+ devices.Add(dev);
+ }
+ CFRelease(cfSet);
+ return devices;
+ }
+
+ private void DeviceAddedCallback(IntPtr context, IntPtr result, IntPtr sender, IntPtr devicePtr)
+ {
+ if (devicePtr == IntPtr.Zero)
+ {
+ return; // Ignore null devices
+ }
+
+ // Create the device and invoke the event
+ var device = new HidMac(devicePtr);
+ device.Setup();
+ OnDeviceInserted?.Invoke(this, new HidEventArgs(device));
+ }
+
+ private void DeviceRemovedCallback(IntPtr context, IntPtr result, IntPtr sender, IntPtr devicePtr)
+ {
+ if (devicePtr == IntPtr.Zero)
+ {
+ return; // Ignore null devices
+ }
+
+ OnDeviceRemoved?.Invoke(this, new HidEventArgs(null));
+ }
+
+ public void Dispose()
+ {
+ if (_hidManager != IntPtr.Zero)
+ {
+ IOHIDManagerUnscheduleFromRunLoop(_hidManager, CFRunLoopGetCurrent(), CFRunLoopModeDefault);
+ IOHIDManagerClose(_hidManager, 0);
+ CFRelease(_hidManager);
+ _hidManager = IntPtr.Zero;
+ }
+ }
+ }
+
+ internal static class NativeMethods
+ {
+ public delegate void IOHIDDeviceCallback(IntPtr context, IntPtr result, IntPtr sender, IntPtr device);
+
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ public static extern IntPtr IOHIDManagerCreate(IntPtr allocator, uint options);
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ public static extern void IOHIDManagerScheduleWithRunLoop(IntPtr manager, IntPtr runLoop, IntPtr runLoopMode);
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ public static extern void IOHIDManagerUnscheduleFromRunLoop(IntPtr manager, IntPtr runLoop, IntPtr runLoopMode);
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ public static extern uint IOHIDManagerOpen(IntPtr manager, uint options);
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ public static extern uint IOHIDManagerClose(IntPtr manager, uint options);
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ public static extern IntPtr IOHIDManagerCopyDevices(IntPtr manager);
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ public static extern void IOHIDManagerRegisterDeviceMatchingCallback(IntPtr manager, IOHIDDeviceCallback callback, IntPtr context);
+ [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
+ public static extern void IOHIDManagerRegisterDeviceRemovalCallback(IntPtr manager, IOHIDDeviceCallback callback, IntPtr context);
+
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ public static extern IntPtr CFRunLoopGetCurrent();
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ public static extern IntPtr CFRetain(IntPtr cf);
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ public static extern void CFRelease(IntPtr cf);
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ public static extern int CFSetGetCount(IntPtr cfset);
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ public static extern void CFSetGetValues(IntPtr cfset, [Out] IntPtr[] values);
+ [DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
+ public static extern IntPtr CFStringCreateWithCString(IntPtr alloc, string str, CFStringEncoding encoding);
+
+ public static readonly IntPtr CFRunLoopModeDefault = CFStringCreateWithCString(
+ IntPtr.Zero, "kCFRunLoopDefaultMode", CFStringEncoding.UTF8
+ );
+
+ public enum CFStringEncoding : uint
+ {
+ UTF8 = 0x08000100
+ }
+ }
+}
diff --git a/EonaCat.HID/Managers/HidManagerWindows.cs b/EonaCat.HID/Managers/HidManagerWindows.cs
new file mode 100644
index 0000000..cf84c7d
--- /dev/null
+++ b/EonaCat.HID/Managers/HidManagerWindows.cs
@@ -0,0 +1,517 @@
+using EonaCat.HID.EventArguments;
+using Microsoft.Win32.SafeHandles;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Runtime.InteropServices;
+using static EonaCat.HID.Managers.Windows.NativeMethods;
+
+namespace EonaCat.HID.Managers.Windows
+{
+ internal sealed class HidManagerWindows : IHidManager, IDisposable
+ {
+ private const int DIGCF_PRESENT = 0x00000002;
+ private const int DIGCF_DEVICEINTERFACE = 0x00000010;
+ private const ushort HID_USAGE_PAGE_GENERIC = 0x01;
+ private const ushort HID_USAGE_GENERIC_MOUSE = 0x02;
+
+ private static Guid GUID_DEVINTERFACE_HID = new Guid("4d1e55b2-f16f-11cf-88cb-001111000030");
+
+ private readonly object _lock = new object();
+
+ // Monitor devices for connect/disconnect
+ private IntPtr _deviceNotificationHandle;
+ private WndProc _windowProcDelegate;
+ private IntPtr _messageWindowHandle;
+
+ public event EventHandler OnDeviceInserted;
+ public event EventHandler OnDeviceRemoved;
+
+ public HidManagerWindows()
+ {
+ InitializeMessageWindow();
+ RegisterForDeviceNotifications();
+ }
+
+ private void InitializeMessageWindow()
+ {
+ // Create hidden window to receive device change messages for insert/remove events
+ _windowProcDelegate = new WndProc(WindowProc);
+ WNDCLASS wc = new WNDCLASS()
+ {
+ lpfnWndProc = _windowProcDelegate,
+ lpszClassName = "HidDeviceNotificationWindow_" + Guid.NewGuid(),
+ style = 0,
+ cbClsExtra = 0,
+ cbWndExtra = 0,
+ hInstance = GetModuleHandle(null),
+ hbrBackground = IntPtr.Zero,
+ hCursor = IntPtr.Zero,
+ hIcon = IntPtr.Zero,
+ lpszMenuName = null
+ };
+
+ ushort classAtom = RegisterClass(ref wc);
+ if (classAtom == 0)
+ {
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to register window class");
+ }
+
+ _messageWindowHandle = CreateWindowEx(
+ 0,
+ wc.lpszClassName,
+ "",
+ 0,
+ 0, 0, 0, 0,
+ IntPtr.Zero,
+ IntPtr.Zero,
+ wc.hInstance,
+ IntPtr.Zero);
+
+ if (_messageWindowHandle == IntPtr.Zero)
+ {
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to create message-only window");
+ }
+ }
+
+ private void RegisterForDeviceNotifications()
+ {
+ DEV_BROADCAST_DEVICEINTERFACE devInterface = new DEV_BROADCAST_DEVICEINTERFACE
+ {
+ dbcc_size = Marshal.SizeOf(),
+ dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE,
+ dbcc_classguid = GUID_DEVINTERFACE_HID
+ };
+
+ IntPtr buffer = Marshal.AllocHGlobal(Marshal.SizeOf(devInterface));
+ Marshal.StructureToPtr(devInterface, buffer, false);
+
+ _deviceNotificationHandle = RegisterDeviceNotification(_messageWindowHandle, buffer, DEVICE_NOTIFY_WINDOW_HANDLE);
+
+ Marshal.FreeHGlobal(buffer);
+
+ if (_deviceNotificationHandle == IntPtr.Zero)
+ {
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to register for device notifications");
+ }
+ }
+
+ private IntPtr WindowProc(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam)
+ {
+ if (msg == WM_DEVICECHANGE)
+ {
+ var eventType = wParam.ToInt32();
+ if (eventType == DBT_DEVICEARRIVAL)
+ {
+ // Device inserted
+ var devBroadcast = Marshal.PtrToStructure(lParam);
+ if (devBroadcast.dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
+ {
+ // Treat all HID devices or filter if needed
+ OnDeviceInserted?.Invoke(this, new HidEventArgs(null));
+ }
+ }
+ else if (eventType == DBT_DEVICEREMOVECOMPLETE)
+ {
+ var devBroadcast = Marshal.PtrToStructure(lParam);
+ if (devBroadcast.dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
+ {
+ OnDeviceRemoved?.Invoke(this, new HidEventArgs(null));
+ }
+ }
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+
+ public IEnumerable Enumerate(ushort? vendorId = null, ushort? productId = null)
+ {
+ var list = new List();
+ IntPtr devInfo = SetupDiGetClassDevs(
+ ref GUID_DEVINTERFACE_HID,
+ null,
+ IntPtr.Zero,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+
+ if (devInfo == IntPtr.Zero || devInfo.ToInt64() == -1)
+ {
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "SetupDiGetClassDevs failed");
+ }
+
+ try
+ {
+ var iface = new SP_DEVICE_INTERFACE_DATA
+ {
+ cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA))
+ };
+
+ for (uint index = 0; ; index++)
+ {
+ bool ok = SetupDiEnumDeviceInterfaces(
+ devInfo, IntPtr.Zero, ref GUID_DEVINTERFACE_HID, index, ref iface);
+
+ if (!ok)
+ {
+ int error = Marshal.GetLastWin32Error();
+ if (error == ERROR_NO_MORE_ITEMS)
+ {
+ break;
+ }
+
+ throw new Win32Exception(error, "SetupDiEnumDeviceInterfaces failed");
+ }
+
+ // Step 1: Get required size
+ uint requiredSize = 0;
+ bool sizeResult = SetupDiGetDeviceInterfaceDetail(
+ devInfo,
+ ref iface,
+ IntPtr.Zero,
+ 0,
+ ref requiredSize,
+ IntPtr.Zero);
+
+ // This call should fail with ERROR_INSUFFICIENT_BUFFER
+ int sizeError = Marshal.GetLastWin32Error();
+ if (sizeError != ERROR_INSUFFICIENT_BUFFER || requiredSize == 0)
+ {
+ continue;
+ }
+
+ // Step 2: Allocate buffer for detail data
+ IntPtr detailDataBuffer = Marshal.AllocHGlobal((int)requiredSize);
+ try
+ {
+ // Step 3: Set cbSize at start of allocated memory
+ // CRITICAL: cbSize must be size of SP_DEVICE_INTERFACE_DETAIL_DATA structure
+ // On x86: 6 bytes (4 for cbSize + 2 for alignment)
+ // On x64: 8 bytes (4 for cbSize + 4 for alignment)
+ int cbSize = IntPtr.Size == 8 ? 8 : 6;
+ Marshal.WriteInt32(detailDataBuffer, cbSize);
+
+ // Step 4: Now get the device interface detail
+ bool success = SetupDiGetDeviceInterfaceDetail(
+ devInfo,
+ ref iface,
+ detailDataBuffer,
+ requiredSize,
+ ref requiredSize,
+ IntPtr.Zero);
+
+ if (!success)
+ {
+ int detailError = Marshal.GetLastWin32Error();
+ throw new Win32Exception(detailError, $"SetupDiGetDeviceInterfaceDetail failed with error {detailError}");
+ }
+
+ // Step 5: Read device path string (starts at offset 4)
+ // The DevicePath is a null-terminated string that starts at offset 4
+ IntPtr pDevicePathName = IntPtr.Add(detailDataBuffer, 4);
+ string devicePath = Marshal.PtrToStringAuto(pDevicePathName);
+
+ if (string.IsNullOrEmpty(devicePath))
+ {
+ continue;
+ }
+
+ // Step 6: Create device, filter and add
+ try
+ {
+ // First try to open the device with minimal access to check if it's accessible
+ using (var testHandle = CreateFile(devicePath, 0, // No access requested
+ FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero))
+ {
+ if (testHandle.IsInvalid)
+ {
+ // Device not accessible, skip it
+ continue;
+ }
+ }
+
+ var device = new HidWindows(devicePath);
+ device.Setup();
+
+ if (vendorId.HasValue && device.VendorId != vendorId.Value ||
+ productId.HasValue && device.ProductId != productId.Value)
+ {
+ device.Dispose();
+ continue;
+ }
+
+ list.Add(device);
+ }
+ catch (UnauthorizedAccessException)
+ {
+ // Device is in use or access denied - skip silently
+ continue;
+ }
+ catch (Exception ex) when (ex.Message.Contains("HidP_GetCaps") ||
+ ex.Message.Contains("The parameter is incorrect") ||
+ ex.Message.Contains("Access is denied"))
+ {
+ // Common HID access failures - skip these devices
+ System.Diagnostics.Debug.WriteLine($"Skipping inaccessible HID device {devicePath}: {ex.Message}");
+ continue;
+ }
+ catch (Exception ex)
+ {
+ // Other unexpected errors - log but continue
+ System.Diagnostics.Debug.WriteLine($"Failed to create device for path {devicePath}: {ex.Message}");
+ continue;
+ }
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(detailDataBuffer);
+ }
+ }
+ }
+ finally
+ {
+ SetupDiDestroyDeviceInfoList(devInfo);
+ }
+
+ return list;
+ }
+
+ public void Dispose()
+ {
+ if (_deviceNotificationHandle != IntPtr.Zero)
+ {
+ UnregisterDeviceNotification(_deviceNotificationHandle);
+ _deviceNotificationHandle = IntPtr.Zero;
+ }
+
+ if (_messageWindowHandle != IntPtr.Zero)
+ {
+ DestroyWindow(_messageWindowHandle);
+ _messageWindowHandle = IntPtr.Zero;
+ }
+ }
+ }
+
+ #region Native Methods and structs
+
+ internal static class NativeMethods
+ {
+ public const int ERROR_INSUFFICIENT_BUFFER = 122;
+ public const int ERROR_NO_MORE_ITEMS = 259;
+
+ public const int FILE_FLAG_OVERLAPPED = 0x40000000;
+ public const int FILE_SHARE_READ = 0x00000001;
+ public const int FILE_SHARE_WRITE = 0x00000002;
+
+ public const int OPEN_EXISTING = 3;
+ public const int GENERIC_READ = unchecked((int)0x80000000);
+ public const int GENERIC_WRITE = 0x40000000;
+ public const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;
+
+ public const int WM_DEVICECHANGE = 0x0219;
+ public const int DBT_DEVICEARRIVAL = 0x8000;
+ public const int DBT_DEVICEREMOVECOMPLETE = 0x8004;
+ public const int DBT_DEVTYP_DEVICEINTERFACE = 5;
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WNDCLASS
+ {
+ public uint style;
+ [MarshalAs(UnmanagedType.FunctionPtr)]
+ public WndProc lpfnWndProc;
+ public int cbClsExtra;
+ public int cbWndExtra;
+ public IntPtr hInstance;
+ public IntPtr hIcon;
+ public IntPtr hCursor;
+ public IntPtr hbrBackground;
+ [MarshalAs(UnmanagedType.LPTStr)]
+ public string lpszMenuName;
+ [MarshalAs(UnmanagedType.LPTStr)]
+ public string lpszClassName;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct DEV_BROADCAST_HDR
+ {
+ public int dbch_size;
+ public int dbch_devicetype;
+ public int dbch_reserved;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct DEV_BROADCAST_DEVICEINTERFACE
+ {
+ public int dbcc_size;
+ public int dbcc_devicetype;
+ public int dbcc_reserved;
+ public Guid dbcc_classguid;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
+ public string dbcc_name;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SP_DEVICE_INTERFACE_DATA
+ {
+ public int cbSize;
+ public Guid InterfaceClassGuid;
+ public int Flags;
+ public IntPtr Reserved;
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ public struct SP_DEVICE_INTERFACE_DETAIL_DATA
+ {
+ public int cbSize;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
+ public string DevicePath;
+ }
+
+
+ // Declare delegate for WndProc
+ public delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern ushort RegisterClass(ref WNDCLASS lpWndClass);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern IntPtr CreateWindowEx(
+ int dwExStyle,
+ string lpClassName,
+ string lpWindowName,
+ int dwStyle,
+ int x, int y, int nWidth, int nHeight,
+ IntPtr hWndParent,
+ IntPtr hMenu,
+ IntPtr hInstance,
+ IntPtr lpParam);
+
+ [DllImport("user32.dll")]
+ public static extern bool DestroyWindow(IntPtr hWnd);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
+ public static extern IntPtr GetModuleHandle(string lpModuleName);
+
+ [DllImport("user32.dll")]
+ public static extern IntPtr DefWindowProc(IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam);
+
+ [DllImport("setupapi.dll", SetLastError = true)]
+ public static extern IntPtr SetupDiGetClassDevs(
+ ref Guid ClassGuid,
+ [MarshalAs(UnmanagedType.LPTStr)] string Enumerator,
+ IntPtr hwndParent,
+ uint Flags);
+
+ [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
+ public static extern bool SetupDiEnumDeviceInterfaces(
+ IntPtr DeviceInfoSet,
+ IntPtr DeviceInfoData,
+ ref Guid InterfaceClassGuid,
+ uint MemberIndex,
+ ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);
+
+ [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
+ public static extern bool SetupDiGetDeviceInterfaceDetail(
+ IntPtr DeviceInfoSet,
+ ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
+ IntPtr DeviceInterfaceDetailData,
+ uint DeviceInterfaceDetailDataSize,
+ ref uint RequiredSize,
+ IntPtr DeviceInfoData);
+
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+ public static extern SafeFileHandle CreateFile(
+ string lpFileName,
+ uint dwDesiredAccess,
+ FileShare dwShareMode,
+ IntPtr lpSecurityAttributes,
+ FileMode dwCreationDisposition,
+ uint dwFlagsAndAttributes,
+ IntPtr hTemplateFile);
+
+ [DllImport("setupapi.dll", SetLastError = true)]
+ public static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, uint Flags);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern bool UnregisterDeviceNotification(IntPtr Handle);
+
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+ public static extern SafeFileHandle CreateFile(
+ string lpFileName,
+ int dwDesiredAccess,
+ int dwShareMode,
+ IntPtr lpSecurityAttributes,
+ int dwCreationDisposition,
+ int dwFlagsAndAttributes,
+ IntPtr hTemplateFile);
+
+ // HID APIs
+ [DllImport("hid.dll", SetLastError = true)]
+ public static extern bool HidD_GetAttributes(IntPtr hidDeviceObject, ref HidDeviceAttributes attributes);
+
+ [DllImport("hid.dll", SetLastError = true)]
+ public static extern bool HidD_GetManufacturerString(IntPtr hidDeviceObject, IntPtr buffer, int bufferLength);
+
+ [DllImport("hid.dll", SetLastError = true)]
+ public static extern bool HidD_GetProductString(IntPtr hidDeviceObject, IntPtr buffer, int bufferLength);
+
+ [DllImport("hid.dll", SetLastError = true)]
+ public static extern bool HidD_GetSerialNumberString(IntPtr hidDeviceObject, IntPtr buffer, int bufferLength);
+
+ [DllImport("hid.dll", SetLastError = true)]
+ public static extern bool HidD_GetPreparsedData(IntPtr hidDeviceObject, out IntPtr preparsedData);
+
+ [DllImport("hid.dll", SetLastError = true)]
+ public static extern bool HidD_FreePreparsedData(IntPtr preparsedData);
+
+ [DllImport("hid.dll", SetLastError = true)]
+ public static extern bool HidD_SetFeature(IntPtr hidDeviceObject, byte[] reportBuffer, int reportBufferLength);
+
+ [DllImport("hid.dll", SetLastError = true)]
+ public static extern bool HidD_GetFeature(IntPtr hidDeviceObject, byte[] reportBuffer, int reportBufferLength);
+
+ [DllImport("hid.dll", SetLastError = true)]
+ public static extern bool HidD_GetInputReport(IntPtr hidDeviceObject, byte[] buffer, int bufferLength);
+
+ // HIDP status
+ [DllImport("hid.dll")]
+ public static extern int HidP_GetCaps(IntPtr preparsedData, out HIDP_CAPS capabilities);
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct HIDP_CAPS
+ {
+ public ushort Usage;
+ public ushort UsagePage;
+ public ushort InputReportByteLength;
+ public ushort OutputReportByteLength;
+ public ushort FeatureReportByteLength;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
+ public ushort[] Reserved;
+ public ushort NumberLinkCollectionNodes;
+ public ushort NumberInputButtonCaps;
+ public ushort NumberInputValueCaps;
+ public ushort NumberInputDataIndices;
+ public ushort NumberOutputButtonCaps;
+ public ushort NumberOutputValueCaps;
+ public ushort NumberOutputDataIndices;
+ public ushort NumberFeatureButtonCaps;
+ public ushort NumberFeatureValueCaps;
+ public ushort NumberFeatureDataIndices;
+ }
+
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct HidDeviceAttributes
+ {
+ public int Size;
+ public ushort VendorID;
+ public ushort ProductID;
+ public ushort VersionNumber;
+ }
+ #endregion
+}
\ No newline at end of file
diff --git a/EonaCat.HID/NativeMethods.cs b/EonaCat.HID/NativeMethods.cs
deleted file mode 100644
index 18a4bc5..0000000
--- a/EonaCat.HID/NativeMethods.cs
+++ /dev/null
@@ -1,225 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-[module: DefaultCharSet(CharSet.Unicode)]
-
-namespace EonaCat.HID
-{
- // This file is part of the EonaCat project(s) which is released under the Apache License.
- // See the LICENSE file or go to https://EonaCat.com/License for full license details.
-
- 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 SECURITY_ATTRIBUTES
- {
- public int nLength;
- public IntPtr lpSecurityDescriptor;
- public bool bInheritHandle;
- }
-
- [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
- internal static extern bool CancelIoEx(IntPtr hFile, IntPtr lpOverlapped);
-
- [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
- internal static extern bool CloseHandle(IntPtr hObject);
-
- [DllImport("kernel32.dll")]
- internal static extern IntPtr CreateEvent(ref SECURITY_ATTRIBUTES securityAttributes, int bManualReset, int bInitialState, string lpName);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- internal static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, ref SECURITY_ATTRIBUTES lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- internal static extern bool ReadFile(IntPtr hFile, IntPtr lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, [In] ref System.Threading.NativeOverlapped lpOverlapped);
-
- [DllImport("kernel32.dll")]
- internal static extern uint WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- internal static extern bool GetOverlappedResult(IntPtr hFile, [In] ref System.Threading.NativeOverlapped lpOverlapped, out uint lpNumberOfBytesTransferred, bool bWait);
-
- [DllImport("kernel32.dll")]
- internal static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, [In] ref System.Threading.NativeOverlapped lpOverlapped);
-
- internal const short DIGCF_PRESENT = 0x2;
- internal const short DIGCF_DEVICEINTERFACE = 0x10;
- internal const int DIGCF_ALLCLASSES = 0x4;
-
- 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 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)]
- 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 uint 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")]
- public static extern unsafe bool SetupDiGetDeviceRegistryProperty(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, int propertyVal, ref int propertyRegDataType, void* propertyBuffer, int propertyBufferSize, ref int requiredSize);
-
- [DllImport("setupapi.dll", SetLastError = true)]
- public static extern unsafe bool SetupDiGetDeviceProperty(IntPtr deviceInfo, ref SP_DEVINFO_DATA deviceInfoData, ref DEVPROPKEY propkey, ref uint propertyDataType, void* propertyBuffer, int propertyBufferSize, ref int requiredSize, uint flags);
-
- [DllImport("setupapi.dll")]
- internal static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet, int memberIndex, ref SP_DEVINFO_DATA deviceInfoData);
-
- [DllImport("setupapi.dll")]
- internal static extern int SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet);
-
- [DllImport("setupapi.dll")]
- internal static extern bool SetupDiEnumDeviceInterfaces(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, ref Guid interfaceClassGuid, int memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);
-
- [DllImport("setupapi.dll")]
- internal static extern IntPtr SetupDiGetClassDevs(ref System.Guid classGuid, string enumerator, IntPtr hwndParent, int flags);
-
- [DllImport("setupapi.dll")]
- internal static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr deviceInfoSet, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr deviceInterfaceDetailData, int deviceInterfaceDetailDataSize, ref int requiredSize, IntPtr deviceInfoData);
-
- [DllImport("setupapi.dll")]
- internal static 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);
-
- [StructLayout(LayoutKind.Sequential)]
- internal struct HIDD_ATTRIBUTES
- {
- internal int Size;
- internal ushort VendorID;
- internal ushort ProductID;
- internal short VersionNumber;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- internal struct Capabilities
- {
- 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;
- }
-
- [DllImport("hid.dll")]
- internal static extern bool HidD_GetAttributes(IntPtr hidDeviceObject, ref HIDD_ATTRIBUTES attributes);
-
- [DllImport("hid.dll")]
- internal static extern bool HidD_GetFeature(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength);
-
- [DllImport("hid.dll")]
- internal static extern bool HidD_GetInputReport(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength);
-
- [DllImport("hid.dll")]
- internal static extern void HidD_GetHidGuid(ref Guid hidGuid);
-
- [DllImport("hid.dll")]
- internal static extern bool HidD_GetPreparsedData(IntPtr hidDeviceObject, ref IntPtr preparsedData);
-
- [DllImport("hid.dll")]
- internal static extern bool HidD_FreePreparsedData(IntPtr preparsedData);
-
- [DllImport("hid.dll")]
- internal static extern bool HidD_SetFeature(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength);
-
- [DllImport("hid.dll")]
- internal static extern bool HidD_SetOutputReport(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength);
-
- [DllImport("hid.dll")]
- internal static extern int HidP_GetCaps(IntPtr preparsedData, ref Capabilities capabilities);
-
- [DllImport("hid.dll")]
- internal static extern bool HidD_GetProductString(IntPtr hidDeviceObject, byte[] lpReportBuffer, int ReportBufferLength);
-
- [DllImport("hid.dll")]
- internal static extern bool HidD_GetManufacturerString(IntPtr hidDeviceObject, byte[] lpReportBuffer, int ReportBufferLength);
-
- [DllImport("hid.dll")]
- internal static extern bool HidD_GetSerialNumberString(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength);
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/ReadDevice.cs b/EonaCat.HID/ReadDevice.cs
deleted file mode 100644
index d36d45b..0000000
--- a/EonaCat.HID/ReadDevice.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System.Threading.Tasks;
-
-namespace EonaCat.HID
-{
- // This file is part of the EonaCat project(s) which is released under the Apache License.
- // See the LICENSE file or go to https://EonaCat.com/License for full license details.
- public class ReadDevice : Device
- {
- internal ReadDevice(string devicePath, string description = null)
- : base(devicePath, description) { }
-
- public DeviceData FastRead(int timeout = 0)
- {
- try
- {
- return ReadData(timeout);
- }
- catch
- {
- return new DeviceData(DeviceData.ReadStatus.ReadError);
- }
- }
-
- public async Task FastReadAsync(int timeout = 0)
- {
- return await Task.Run(() => FastRead(timeout));
- }
-
- public Report FastReadReport(int timeout = 0)
- {
- return new Report(Capabilities.InputReportByteLength, FastRead(timeout));
- }
-
- public async Task FastReadReportAsync(int timeout = 0)
- {
- return await Task.Run(() => FastReadReport(timeout));
- }
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/ReadEnumerator.cs b/EonaCat.HID/ReadEnumerator.cs
deleted file mode 100644
index 3524558..0000000
--- a/EonaCat.HID/ReadEnumerator.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-
-namespace EonaCat.HID
-{
- // This file is part of the EonaCat project(s) which is released under the Apache License.
- // See the LICENSE file or go to https://EonaCat.com/License for full license details.
- public class ReadEnumerator : IEnumerator
- {
- public bool IsConnected(string devicePath)
- {
- return Devices.IsConnected(devicePath);
- }
-
- public IDevice GetDevice(string devicePath)
- {
- return Enumerate(devicePath).FirstOrDefault();
- }
-
- public IEnumerable Enumerate()
- {
- return Devices.EnumerateDevices().
- Select(d => new ReadDevice(d.Path, d.Description) as IDevice);
- }
-
- public IEnumerable Enumerate(string devicePath)
- {
- return Devices.EnumerateDevices().Where(x => x.Path == devicePath).
- Select(d => new ReadDevice(d.Path, d.Description) as IDevice);
- }
-
- public IEnumerable Enumerate(int vendorId, params int[] productIds)
- {
- return Devices.EnumerateDevices().Select(d => new ReadDevice(d.Path, d.Description)).
- Where(f => f.Info.VendorId == vendorId && productIds.Contains(f.Info.ProductId)).
- Select(d => d as IDevice);
- }
-
- public IEnumerable Enumerate(int vendorId)
- {
- return Devices.EnumerateDevices().Select(d => new ReadDevice(d.Path, d.Description)).
- Where(f => f.Info.VendorId == vendorId).
- Select(d => d as IDevice);
- }
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/Report.cs b/EonaCat.HID/Report.cs
deleted file mode 100644
index 19d2a34..0000000
--- a/EonaCat.HID/Report.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using System;
-
-namespace EonaCat.HID
-{
- // This file is part of the EonaCat project(s) which is released under the Apache License.
- // See the LICENSE file or go to https://EonaCat.com/License for full license details.
- public class Report
- {
- private byte _reportId;
- private byte[] _data;
- private readonly DeviceData.ReadStatus _status;
-
- public Report(int reportSize)
- {
- if (reportSize > 0)
- {
- _data = new byte[reportSize - 1];
- }
- else
- {
- Exists = false;
- return;
- }
- }
-
- public Report(int reportSize, DeviceData deviceData)
- {
- _status = deviceData.Status;
- if (reportSize > 0)
- {
- _data = new byte[reportSize - 1];
- }
- else
- {
- Exists = false;
- return;
- }
-
- if (deviceData?.Data == null || deviceData.Data.Length == 0)
- {
- Exists = false;
- return;
- }
-
- _reportId = deviceData.Data[0];
- Exists = true;
-
- var dataLength = Math.Min(deviceData.Data.Length - 1, reportSize - 1);
- if (dataLength > 0)
- {
- Array.Copy(deviceData.Data, 1, _data, 0, dataLength);
- }
- }
-
- public bool Exists { get; private set; }
- public DeviceData.ReadStatus ReadStatus => _status;
-
- public byte ReportId
- {
- get => _reportId;
- set
- {
- _reportId = value;
- Exists = true;
- }
- }
-
- public byte[] Data
- {
- get => _data;
- set
- {
- _data = value;
- Exists = true;
- }
- }
-
- public byte[] GetBytes()
- {
- var data = new byte[_data.Length + 1];
- data[0] = _reportId;
- Array.Copy(_data, 0, data, 1, _data.Length);
- return data;
- }
- }
-}
\ No newline at end of file
diff --git a/EonaCat.HID/StateAsync.cs b/EonaCat.HID/StateAsync.cs
deleted file mode 100644
index 7d1ffae..0000000
--- a/EonaCat.HID/StateAsync.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-namespace EonaCat.HID
-{
- // This file is part of the EonaCat project(s) which is released under the Apache License.
- // See the LICENSE file or go to https://EonaCat.com/License for full license details.
- public class StateAsync
- {
- private readonly object _callerDelegate;
- private readonly object _callbackDelegate;
-
- public StateAsync(object callerDelegate, object callbackDelegate)
- {
- _callerDelegate = callerDelegate;
- _callbackDelegate = callbackDelegate;
- }
-
- public object CallerDelegate
- { get { return _callerDelegate; } }
- public object CallbackDelegate
- { get { return _callbackDelegate; } }
- }
-}
\ No newline at end of file