Updated console message loop
This commit is contained in:
@@ -1,115 +1,116 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProjectGuid>{61994020-DB89-4621-BA4B-7347A2142CFF}</ProjectGuid>
|
<ProjectGuid>{61994020-DB89-4621-BA4B-7347A2142CFF}</ProjectGuid>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>EonaCat.HID</RootNamespace>
|
<RootNamespace>EonaCat.HID</RootNamespace>
|
||||||
<AssemblyName>EonaCat HID Analyzer</AssemblyName>
|
<AssemblyName>EonaCat HID Analyzer</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ApplicationIcon>
|
<ApplicationIcon>
|
||||||
</ApplicationIcon>
|
</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Management" />
|
<Reference Include="System.Management" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Deployment" />
|
<Reference Include="System.Deployment" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
<Reference Include="WindowsBase" />
|
||||||
<ItemGroup>
|
</ItemGroup>
|
||||||
<Compile Include="AboutBox.cs">
|
<ItemGroup>
|
||||||
<SubType>Form</SubType>
|
<Compile Include="AboutBox.cs">
|
||||||
</Compile>
|
<SubType>Form</SubType>
|
||||||
<Compile Include="AboutBox.designer.cs">
|
</Compile>
|
||||||
<DependentUpon>AboutBox.cs</DependentUpon>
|
<Compile Include="AboutBox.designer.cs">
|
||||||
</Compile>
|
<DependentUpon>AboutBox.cs</DependentUpon>
|
||||||
<Compile Include="MainForm.cs">
|
</Compile>
|
||||||
<SubType>Form</SubType>
|
<Compile Include="MainForm.cs">
|
||||||
</Compile>
|
<SubType>Form</SubType>
|
||||||
<Compile Include="MainForm.Designer.cs">
|
</Compile>
|
||||||
<DependentUpon>MainForm.cs</DependentUpon>
|
<Compile Include="MainForm.Designer.cs">
|
||||||
</Compile>
|
<DependentUpon>MainForm.cs</DependentUpon>
|
||||||
<Compile Include="Program.cs" />
|
</Compile>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<EmbeddedResource Include="AboutBox.resx">
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<DependentUpon>AboutBox.cs</DependentUpon>
|
<EmbeddedResource Include="AboutBox.resx">
|
||||||
<SubType>Designer</SubType>
|
<DependentUpon>AboutBox.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
<SubType>Designer</SubType>
|
||||||
<EmbeddedResource Include="MainForm.resx">
|
</EmbeddedResource>
|
||||||
<DependentUpon>MainForm.cs</DependentUpon>
|
<EmbeddedResource Include="MainForm.resx">
|
||||||
<SubType>Designer</SubType>
|
<DependentUpon>MainForm.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
<SubType>Designer</SubType>
|
||||||
<EmbeddedResource Include="Properties\Resources.resx">
|
</EmbeddedResource>
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
<SubType>Designer</SubType>
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
</EmbeddedResource>
|
<SubType>Designer</SubType>
|
||||||
<Compile Include="Properties\Resources.Designer.cs">
|
</EmbeddedResource>
|
||||||
<AutoGen>True</AutoGen>
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<AutoGen>True</AutoGen>
|
||||||
<DesignTime>True</DesignTime>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
<DesignTime>True</DesignTime>
|
||||||
<None Include="Properties\Settings.settings">
|
</Compile>
|
||||||
<Generator>SettingsSingleFileGenerator</Generator>
|
<None Include="Properties\Settings.settings">
|
||||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
</None>
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
<Compile Include="Properties\Settings.Designer.cs">
|
</None>
|
||||||
<AutoGen>True</AutoGen>
|
<Compile Include="Properties\Settings.Designer.cs">
|
||||||
<DependentUpon>Settings.settings</DependentUpon>
|
<AutoGen>True</AutoGen>
|
||||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
</Compile>
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
</ItemGroup>
|
</Compile>
|
||||||
<ItemGroup>
|
</ItemGroup>
|
||||||
<None Include="App.config" />
|
<ItemGroup>
|
||||||
</ItemGroup>
|
<None Include="App.config" />
|
||||||
<ItemGroup>
|
</ItemGroup>
|
||||||
<Content Include="icon.ico" />
|
<ItemGroup>
|
||||||
</ItemGroup>
|
<Content Include="icon.ico" />
|
||||||
<ItemGroup>
|
</ItemGroup>
|
||||||
<ProjectReference Include="..\EonaCat.HID\EonaCat.HID.csproj">
|
<ItemGroup>
|
||||||
<Project>{00403bd6-7a26-4971-29d3-8a7849aac770}</Project>
|
<ProjectReference Include="..\EonaCat.HID\EonaCat.HID.csproj">
|
||||||
<Name>EonaCat.HID</Name>
|
<Project>{00403bd6-7a26-4971-29d3-8a7849aac770}</Project>
|
||||||
</ProjectReference>
|
<Name>EonaCat.HID</Name>
|
||||||
</ItemGroup>
|
</ProjectReference>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
<Target Name="BeforeBuild">
|
<Target Name="BeforeBuild">
|
||||||
</Target>
|
</Target>
|
||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
</Target>
|
</Target>
|
||||||
-->
|
-->
|
||||||
</Project>
|
</Project>
|
@@ -10,6 +10,7 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
|
||||||
namespace EonaCat.HID.Analyzer
|
namespace EonaCat.HID.Analyzer
|
||||||
{
|
{
|
||||||
@@ -42,7 +43,8 @@ namespace EonaCat.HID.Analyzer
|
|||||||
|
|
||||||
public async Task RefreshDevicesAsync(ushort? vendorId = null, ushort? productId = null)
|
public async Task RefreshDevicesAsync(ushort? vendorId = null, ushort? productId = null)
|
||||||
{
|
{
|
||||||
_deviceList = await _deviceManager.EnumerateAsync(vendorId, productId).ConfigureAwait(false);
|
toolStripStatusLabel1.Text = "Refreshing devices, this may take a while...";
|
||||||
|
_deviceList = await _deviceManager.EnumerateAsync(vendorId, productId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MainForm_Load(object sender, System.EventArgs e)
|
private void MainForm_Load(object sender, System.EventArgs e)
|
||||||
@@ -57,12 +59,12 @@ namespace EonaCat.HID.Analyzer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MainForm_Shown(object sender, System.EventArgs e)
|
private async void MainForm_Shown(object sender, System.EventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RefreshDevicesAsync();
|
await RefreshDevicesAsync().ConfigureAwait(false);
|
||||||
UpdateDeviceList();
|
UpdateDeviceListAsync();
|
||||||
toolStripStatusLabel1.Text = "Please select device and click open to start.";
|
toolStripStatusLabel1.Text = "Please select device and click open to start.";
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -94,44 +96,62 @@ namespace EonaCat.HID.Analyzer
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateDeviceList()
|
private async Task UpdateDeviceListAsync()
|
||||||
{
|
{
|
||||||
dataGridView1.SelectionChanged -= DataGridView1_SelectionChanged;
|
if (_deviceList == null || !_deviceList.Any())
|
||||||
dataGridView1.Rows.Clear();
|
{
|
||||||
|
return;
|
||||||
for (int i = 0; i < _deviceList.Count(); i++)
|
}
|
||||||
{
|
|
||||||
IHid device = _deviceList.ElementAt(i);
|
// Capture the data in a local variable so the UI update can happen in one go
|
||||||
|
var rows = new List<string[]>();
|
||||||
var deviceName = "";
|
for (int i = 0; i < _deviceList.Count(); i++)
|
||||||
var deviceManufacturer = "";
|
{
|
||||||
var deviceSerialNumber = "";
|
IHid device = _deviceList.ElementAt(i);
|
||||||
|
|
||||||
deviceName = device.ProductName;
|
var row = new string[]
|
||||||
deviceManufacturer = device.Manufacturer;
|
{
|
||||||
deviceSerialNumber = device.SerialNumber;
|
(i + 1).ToString(),
|
||||||
var isWritingSupported = device.IsWritingSupport;
|
device.ProductName,
|
||||||
var isReadingSupported = device.IsReadingSupport;
|
device.Manufacturer,
|
||||||
|
device.SerialNumber,
|
||||||
var row = new string[]
|
device.IsReadingSupport.ToString(),
|
||||||
{
|
device.IsWritingSupport.ToString(),
|
||||||
(i + 1).ToString(),
|
device.InputReportByteLength.ToString(),
|
||||||
deviceName,
|
device.OutputReportByteLength.ToString(),
|
||||||
deviceManufacturer,
|
device.FeatureReportByteLength.ToString(),
|
||||||
deviceSerialNumber,
|
$"Vendor:{device.VendorId:X4} Product:{device.ProductId:X4} Revision:{device.VendorId:X4}",
|
||||||
isReadingSupported.ToString(),
|
device.DevicePath
|
||||||
isWritingSupported.ToString(),
|
};
|
||||||
device.InputReportByteLength.ToString(),
|
|
||||||
device.OutputReportByteLength.ToString(),
|
rows.Add(row);
|
||||||
device.FeatureReportByteLength.ToString(),
|
}
|
||||||
$"Vendor:{device.VendorId:X4} Product:{device.ProductId:X4} Revision:{device.VendorId:X4}",
|
|
||||||
device.DevicePath
|
if (dataGridView1.InvokeRequired)
|
||||||
};
|
{
|
||||||
|
dataGridView1.Invoke(new Action(() =>
|
||||||
dataGridView1.Rows.Add(row);
|
{
|
||||||
}
|
dataGridView1.SelectionChanged -= DataGridView1_SelectionChanged;
|
||||||
dataGridView1.SelectionChanged += DataGridView1_SelectionChanged;
|
dataGridView1.Rows.Clear();
|
||||||
DataGridView1_SelectionChanged(this, null);
|
foreach (var row in rows)
|
||||||
|
{
|
||||||
|
dataGridView1.Rows.Add(row);
|
||||||
|
}
|
||||||
|
dataGridView1.SelectionChanged += DataGridView1_SelectionChanged;
|
||||||
|
DataGridView1_SelectionChanged(this, null);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dataGridView1.SelectionChanged -= DataGridView1_SelectionChanged;
|
||||||
|
dataGridView1.Rows.Clear();
|
||||||
|
foreach (var row in rows)
|
||||||
|
{
|
||||||
|
dataGridView1.Rows.Add(row);
|
||||||
|
}
|
||||||
|
dataGridView1.SelectionChanged += DataGridView1_SelectionChanged;
|
||||||
|
DataGridView1_SelectionChanged(this, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PopupException(string message, string caption = "Exception")
|
private void PopupException(string message, string caption = "Exception")
|
||||||
@@ -178,12 +198,12 @@ namespace EonaCat.HID.Analyzer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToolStripButtonReload_Click(object sender, EventArgs e)
|
private async void ToolStripButtonReload_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RefreshDevicesAsync();
|
await RefreshDevicesAsync().ConfigureAwait(false);
|
||||||
UpdateDeviceList();
|
UpdateDeviceListAsync();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -191,7 +211,7 @@ namespace EonaCat.HID.Analyzer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToolStripButtonFilter_Click(object sender, EventArgs e)
|
private async void ToolStripButtonFilter_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -203,8 +223,8 @@ namespace EonaCat.HID.Analyzer
|
|||||||
vid = ushort.Parse(str[0], NumberStyles.AllowHexSpecifier);
|
vid = ushort.Parse(str[0], NumberStyles.AllowHexSpecifier);
|
||||||
pid = ushort.Parse(str[1], NumberStyles.AllowHexSpecifier);
|
pid = ushort.Parse(str[1], NumberStyles.AllowHexSpecifier);
|
||||||
}
|
}
|
||||||
RefreshDevicesAsync(vid, pid);
|
await RefreshDevicesAsync(vid, pid).ConfigureAwait(false);
|
||||||
UpdateDeviceList();
|
UpdateDeviceListAsync();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -341,12 +361,12 @@ namespace EonaCat.HID.Analyzer
|
|||||||
}
|
}
|
||||||
|
|
||||||
var report = await _device.ReadInputReportAsync();
|
var report = await _device.ReadInputReportAsync();
|
||||||
if (report == null || report.Data.Length < 2)
|
if (report == null || report.Data.Length < 2)
|
||||||
{
|
{
|
||||||
AppendEventLog("Received report is null or is too short to contain a valid Report ID.", Color.Red);
|
AppendEventLog("Received report is null or is too short to contain a valid Report ID.", Color.Red);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var str = string.Format("Rx Input Report [{0}] <-- {1}", report.Data.Length, BitConverter.ToString(report.Data));
|
var str = string.Format("Rx Input Report [{0}] <-- {1}", report.Data.Length, BitConverter.ToString(report.Data));
|
||||||
AppendEventLog(str, Color.Blue);
|
AppendEventLog(str, Color.Blue);
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,7 @@ namespace EonaCat.HID.Example
|
|||||||
Console.WriteLine($"Removed Device --> VID: {e.Device.VendorId:X4}, PID: {e.Device.ProductId:X4}");
|
Console.WriteLine($"Removed Device --> VID: {e.Device.VendorId:X4}, PID: {e.Device.ProductId:X4}");
|
||||||
};
|
};
|
||||||
|
|
||||||
RefreshDevicesAsync();
|
await RefreshDevicesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
if (!_deviceList.Any())
|
if (!_deviceList.Any())
|
||||||
{
|
{
|
||||||
|
@@ -1,55 +1,56 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net46;net6.0</TargetFrameworks>
|
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Company>EonaCat (Jeroen Saey)</Company>
|
<Company>EonaCat (Jeroen Saey)</Company>
|
||||||
<Copyright>Copyright 2024 EonaCat (Jeroen Saey)</Copyright>
|
<Copyright>Copyright 2024 EonaCat (Jeroen Saey)</Copyright>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<PackageId>EonaCat.HID</PackageId>
|
<PackageId>EonaCat.HID</PackageId>
|
||||||
<Version>1.0.5</Version>
|
<Version>1.0.6</Version>
|
||||||
<Title>EonaCat.HID</Title>
|
<Title>EonaCat.HID</Title>
|
||||||
<Authors>EonaCat (Jeroen Saey)</Authors>
|
<Authors>EonaCat (Jeroen Saey)</Authors>
|
||||||
<Description>HID Devices</Description>
|
<Description>HID Devices</Description>
|
||||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
<PackageProjectUrl></PackageProjectUrl>
|
<PackageProjectUrl></PackageProjectUrl>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
<PackageProjectUrl>https://www.nuget.org/packages/EonaCat.HID/</PackageProjectUrl>
|
<PackageProjectUrl>https://www.nuget.org/packages/EonaCat.HID/</PackageProjectUrl>
|
||||||
<PackageTags>usb; hid; Jeroen;Saey</PackageTags>
|
<PackageTags>usb; hid; Jeroen;Saey</PackageTags>
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\icon.png">
|
<None Include="..\icon.png">
|
||||||
<Pack>True</Pack>
|
<Pack>True</Pack>
|
||||||
<PackagePath>\</PackagePath>
|
<PackagePath>\</PackagePath>
|
||||||
</None>
|
</None>
|
||||||
<None Include="..\LICENSE">
|
<None Include="..\LICENSE">
|
||||||
<Pack>True</Pack>
|
<Pack>True</Pack>
|
||||||
<PackagePath>\</PackagePath>
|
<PackagePath>\</PackagePath>
|
||||||
</None>
|
</None>
|
||||||
<None Include="..\README.md">
|
<None Include="..\README.md">
|
||||||
<Pack>True</Pack>
|
<Pack>True</Pack>
|
||||||
<PackagePath>\</PackagePath>
|
<PackagePath>\</PackagePath>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" />
|
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||||
</ItemGroup>
|
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="System.Windows.Forms">
|
<ItemGroup>
|
||||||
<HintPath>..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Windows.Forms.dll</HintPath>
|
<None Update="icon.png">
|
||||||
</Reference>
|
<Pack>True</Pack>
|
||||||
</ItemGroup>
|
<PackagePath>\</PackagePath>
|
||||||
|
</None>
|
||||||
<ItemGroup>
|
</ItemGroup>
|
||||||
<None Update="icon.png">
|
|
||||||
<Pack>True</Pack>
|
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
|
||||||
<PackagePath>\</PackagePath>
|
<PackageReference Include="System.Reflection.DispatchProxy">
|
||||||
</None>
|
<Version>4.8.2</Version>
|
||||||
</ItemGroup>
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
|
||||||
|
</Project>
|
||||||
|
@@ -50,7 +50,9 @@ namespace EonaCat.HID
|
|||||||
{
|
{
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
return new Managers.Windows.HidManagerWindows();
|
var deviceManager = new Managers.Windows.HidManagerWindows();
|
||||||
|
deviceManager.Start();
|
||||||
|
return deviceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
@@ -59,10 +59,12 @@ namespace EonaCat.HID
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
if (_stream != null || _isOpen)
|
if (_stream != null || _isOpen)
|
||||||
return;
|
{
|
||||||
|
return;
|
||||||
// Open HID device in non-blocking read/write mode
|
}
|
||||||
|
|
||||||
|
// Open HID device in non-blocking read/write mode
|
||||||
int fd = NativeMethods.open(_devicePath, NativeMethods.O_RDWR | NativeMethods.O_NONBLOCK);
|
int fd = NativeMethods.open(_devicePath, NativeMethods.O_RDWR | NativeMethods.O_NONBLOCK);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
{
|
{
|
||||||
@@ -262,10 +264,12 @@ namespace EonaCat.HID
|
|||||||
|
|
||||||
public async Task SendFeatureReportAsync(HidReport report)
|
public async Task SendFeatureReportAsync(HidReport report)
|
||||||
{
|
{
|
||||||
if (report == null)
|
if (report == null)
|
||||||
throw new ArgumentNullException(nameof(report));
|
{
|
||||||
|
throw new ArgumentNullException(nameof(report));
|
||||||
// Prepare full report buffer: [reportId][reportData...]
|
}
|
||||||
|
|
||||||
|
// Prepare full report buffer: [reportId][reportData...]
|
||||||
int size = 1 + (report.Data?.Length ?? 0);
|
int size = 1 + (report.Data?.Length ?? 0);
|
||||||
byte[] buffer = new byte[size];
|
byte[] buffer = new byte[size];
|
||||||
buffer[0] = report.ReportId;
|
buffer[0] = report.ReportId;
|
||||||
@@ -298,8 +302,11 @@ namespace EonaCat.HID
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (unmanagedBuffer != IntPtr.Zero)
|
if (unmanagedBuffer != IntPtr.Zero)
|
||||||
Marshal.FreeHGlobal(unmanagedBuffer);
|
{
|
||||||
|
Marshal.FreeHGlobal(unmanagedBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
NativeMethods.close(fd);
|
NativeMethods.close(fd);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -342,15 +349,20 @@ namespace EonaCat.HID
|
|||||||
|
|
||||||
byte actualReportId = actualBuffer.Length > 0 ? actualBuffer[0] : (byte)0;
|
byte actualReportId = actualBuffer.Length > 0 ? actualBuffer[0] : (byte)0;
|
||||||
byte[] reportData = actualBuffer.Length > 1 ? new byte[actualBuffer.Length - 1] : Array.Empty<byte>();
|
byte[] reportData = actualBuffer.Length > 1 ? new byte[actualBuffer.Length - 1] : Array.Empty<byte>();
|
||||||
if (reportData.Length > 0)
|
if (reportData.Length > 0)
|
||||||
Array.Copy(actualBuffer, 1, reportData, 0, reportData.Length);
|
{
|
||||||
|
Array.Copy(actualBuffer, 1, reportData, 0, reportData.Length);
|
||||||
|
}
|
||||||
|
|
||||||
return new HidReport(actualReportId, reportData);
|
return new HidReport(actualReportId, reportData);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (bufPtr != IntPtr.Zero)
|
if (bufPtr != IntPtr.Zero)
|
||||||
Marshal.FreeHGlobal(bufPtr);
|
{
|
||||||
|
Marshal.FreeHGlobal(bufPtr);
|
||||||
|
}
|
||||||
|
|
||||||
NativeMethods.close(fd);
|
NativeMethods.close(fd);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -77,7 +77,9 @@ namespace EonaCat.HID
|
|||||||
public void Open()
|
public void Open()
|
||||||
{
|
{
|
||||||
if (_isOpen)
|
if (_isOpen)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FileAccess access = FileAccess.ReadWrite;
|
FileAccess access = FileAccess.ReadWrite;
|
||||||
SafeFileHandle handle = TryOpenDevice(GENERIC_READ | GENERIC_WRITE);
|
SafeFileHandle handle = TryOpenDevice(GENERIC_READ | GENERIC_WRITE);
|
||||||
@@ -86,14 +88,18 @@ namespace EonaCat.HID
|
|||||||
{
|
{
|
||||||
handle = TryOpenDevice(GENERIC_READ);
|
handle = TryOpenDevice(GENERIC_READ);
|
||||||
if (handle != null && !handle.IsInvalid)
|
if (handle != null && !handle.IsInvalid)
|
||||||
|
{
|
||||||
access = FileAccess.Read;
|
access = FileAccess.Read;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((handle == null || handle.IsInvalid) && Environment.Is64BitOperatingSystem)
|
if ((handle == null || handle.IsInvalid) && Environment.Is64BitOperatingSystem)
|
||||||
{
|
{
|
||||||
handle = TryOpenDevice(GENERIC_WRITE);
|
handle = TryOpenDevice(GENERIC_WRITE);
|
||||||
if (handle != null && !handle.IsInvalid)
|
if (handle != null && !handle.IsInvalid)
|
||||||
|
{
|
||||||
access = FileAccess.Write;
|
access = FileAccess.Write;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handle == null || handle.IsInvalid)
|
if (handle == null || handle.IsInvalid)
|
||||||
@@ -110,12 +116,16 @@ namespace EonaCat.HID
|
|||||||
|
|
||||||
// HID descriptor
|
// HID descriptor
|
||||||
if (!HidD_GetPreparsedData(_deviceHandle.DangerousGetHandle(), out _preparsedData))
|
if (!HidD_GetPreparsedData(_deviceHandle.DangerousGetHandle(), out _preparsedData))
|
||||||
|
{
|
||||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed HidD_GetPreparsedData");
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed HidD_GetPreparsedData");
|
||||||
|
}
|
||||||
|
|
||||||
HIDP_CAPS caps;
|
HIDP_CAPS caps;
|
||||||
int capsRes = HidP_GetCaps(_preparsedData, out caps);
|
int capsRes = HidP_GetCaps(_preparsedData, out caps);
|
||||||
if (capsRes != NativeMethods.HIDP_STATUS_SUCCESS)
|
if (capsRes != NativeMethods.HIDP_STATUS_SUCCESS)
|
||||||
|
{
|
||||||
throw new Win32Exception(capsRes, "Failed HidP_GetCaps");
|
throw new Win32Exception(capsRes, "Failed HidP_GetCaps");
|
||||||
|
}
|
||||||
|
|
||||||
InputReportByteLength = caps.InputReportByteLength;
|
InputReportByteLength = caps.InputReportByteLength;
|
||||||
OutputReportByteLength = caps.OutputReportByteLength;
|
OutputReportByteLength = caps.OutputReportByteLength;
|
||||||
@@ -304,7 +314,9 @@ namespace EonaCat.HID
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (report == null)
|
if (report == null)
|
||||||
|
{
|
||||||
throw new ArgumentNullException(nameof(report));
|
throw new ArgumentNullException(nameof(report));
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare buffer with ReportId + Data
|
// Prepare buffer with ReportId + Data
|
||||||
var data = new byte[1 + report.Data.Length];
|
var data = new byte[1 + report.Data.Length];
|
||||||
|
@@ -107,9 +107,11 @@ namespace EonaCat.HID.Managers.Mac
|
|||||||
|
|
||||||
private void DeviceAddedCallback(IntPtr context, IntPtr result, IntPtr sender, IntPtr devicePtr)
|
private void DeviceAddedCallback(IntPtr context, IntPtr result, IntPtr sender, IntPtr devicePtr)
|
||||||
{
|
{
|
||||||
if (devicePtr == IntPtr.Zero)
|
if (devicePtr == IntPtr.Zero)
|
||||||
return;
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var device = new HidMac(devicePtr);
|
var device = new HidMac(devicePtr);
|
||||||
@@ -125,9 +127,11 @@ namespace EonaCat.HID.Managers.Mac
|
|||||||
|
|
||||||
private void DeviceRemovedCallback(IntPtr context, IntPtr result, IntPtr sender, IntPtr devicePtr)
|
private void DeviceRemovedCallback(IntPtr context, IntPtr result, IntPtr sender, IntPtr devicePtr)
|
||||||
{
|
{
|
||||||
if (devicePtr == IntPtr.Zero)
|
if (devicePtr == IntPtr.Zero)
|
||||||
return;
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var device = new HidMac(devicePtr);
|
var device = new HidMac(devicePtr);
|
||||||
|
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using static EonaCat.HID.Managers.Windows.NativeMethods;
|
using static EonaCat.HID.Managers.Windows.NativeMethods;
|
||||||
|
|
||||||
@@ -20,7 +21,10 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
private const ushort HID_USAGE_PAGE_GENERIC = 0x01;
|
private const ushort HID_USAGE_PAGE_GENERIC = 0x01;
|
||||||
private const ushort HID_USAGE_GENERIC_MOUSE = 0x02;
|
private const ushort HID_USAGE_GENERIC_MOUSE = 0x02;
|
||||||
|
|
||||||
private static Guid GUID_DEVINTERFACE_HID = new Guid("4d1e55b2-f16f-11cf-88cb-001111000030");
|
private static Guid GUID_DEVINTERFACE_HID = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030");
|
||||||
|
|
||||||
|
private static readonly IntPtr HWND_MESSAGE = new IntPtr(-3);
|
||||||
|
private const string WINDOW_CLASS_NAME = "EonaCat_HidDeviceNotificationWindow";
|
||||||
|
|
||||||
private readonly object _lock = new object();
|
private readonly object _lock = new object();
|
||||||
|
|
||||||
@@ -28,15 +32,15 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
private IntPtr _deviceNotificationHandle;
|
private IntPtr _deviceNotificationHandle;
|
||||||
private WndProc _windowProcDelegate;
|
private WndProc _windowProcDelegate;
|
||||||
private IntPtr _messageWindowHandle;
|
private IntPtr _messageWindowHandle;
|
||||||
private readonly Dictionary<string, IHid> _knownDevices = new();
|
private readonly Dictionary<string, IHid> _knownDevices = new();
|
||||||
|
private Thread _messageThread;
|
||||||
|
|
||||||
public event EventHandler<HidEventArgs> OnDeviceInserted;
|
public event EventHandler<HidEventArgs> OnDeviceInserted;
|
||||||
public event EventHandler<HidEventArgs> OnDeviceRemoved;
|
public event EventHandler<HidEventArgs> OnDeviceRemoved;
|
||||||
|
|
||||||
public HidManagerWindows()
|
public HidManagerWindows()
|
||||||
{
|
{
|
||||||
InitializeMessageWindow();
|
// Do nothing
|
||||||
RegisterForDeviceNotifications();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeMessageWindow()
|
private void InitializeMessageWindow()
|
||||||
@@ -46,7 +50,7 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
WNDCLASS wc = new WNDCLASS()
|
WNDCLASS wc = new WNDCLASS()
|
||||||
{
|
{
|
||||||
lpfnWndProc = _windowProcDelegate,
|
lpfnWndProc = _windowProcDelegate,
|
||||||
lpszClassName = "HidDeviceNotificationWindow_" + Guid.NewGuid(),
|
lpszClassName = WINDOW_CLASS_NAME,
|
||||||
style = 0,
|
style = 0,
|
||||||
cbClsExtra = 0,
|
cbClsExtra = 0,
|
||||||
cbWndExtra = 0,
|
cbWndExtra = 0,
|
||||||
@@ -61,24 +65,79 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
if (classAtom == 0)
|
if (classAtom == 0)
|
||||||
{
|
{
|
||||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to register window class");
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to register window class");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_messageWindowHandle = CreateWindowEx(
|
||||||
|
0,
|
||||||
|
wc.lpszClassName,
|
||||||
|
"",
|
||||||
|
0,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
HWND_MESSAGE,
|
||||||
|
IntPtr.Zero,
|
||||||
|
wc.hInstance,
|
||||||
|
IntPtr.Zero);
|
||||||
|
|
||||||
_messageWindowHandle = CreateWindowEx(
|
|
||||||
0,
|
|
||||||
wc.lpszClassName,
|
|
||||||
"",
|
|
||||||
0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
IntPtr.Zero,
|
|
||||||
IntPtr.Zero,
|
|
||||||
wc.hInstance,
|
|
||||||
IntPtr.Zero);
|
|
||||||
|
|
||||||
if (_messageWindowHandle == IntPtr.Zero)
|
if (_messageWindowHandle == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to create message-only window");
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to create message-only window");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
_messageThread = new Thread(MessageThreadProc)
|
||||||
|
{
|
||||||
|
IsBackground = true
|
||||||
|
};
|
||||||
|
_messageThread.SetApartmentState(ApartmentState.STA);
|
||||||
|
_messageThread.Start();
|
||||||
|
}
|
||||||
|
public Task StartAsync() => Task.Run(Start);
|
||||||
|
|
||||||
|
private void OnWpfMessage(object sender, dynamic e)
|
||||||
|
{
|
||||||
|
if (e.Message == WM_DEVICECHANGE)
|
||||||
|
{
|
||||||
|
WindowProc(IntPtr.Zero, e.Message, e.WParam, e.LParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MessageThreadProc()
|
||||||
|
{
|
||||||
|
InitializeMessageWindow();
|
||||||
|
RegisterForDeviceNotifications();
|
||||||
|
|
||||||
|
// Start message loop
|
||||||
|
MSG msg;
|
||||||
|
while (GetMessage(out msg, IntPtr.Zero, 0, 0))
|
||||||
|
{
|
||||||
|
TranslateMessage(ref msg);
|
||||||
|
DispatchMessage(ref msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void StartMessageLoop()
|
||||||
|
{
|
||||||
|
Thread thread = new Thread(() =>
|
||||||
|
{
|
||||||
|
InitializeMessageWindow();
|
||||||
|
RegisterForDeviceNotifications();
|
||||||
|
|
||||||
|
MSG msg;
|
||||||
|
while (GetMessage(out msg, IntPtr.Zero, 0, 0))
|
||||||
|
{
|
||||||
|
TranslateMessage(ref msg);
|
||||||
|
DispatchMessage(ref msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
thread.IsBackground = true;
|
||||||
|
thread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void RegisterForDeviceNotifications()
|
private void RegisterForDeviceNotifications()
|
||||||
{
|
{
|
||||||
@@ -102,7 +161,7 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IntPtr WindowProc(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam)
|
internal IntPtr WindowProc(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam)
|
||||||
{
|
{
|
||||||
if (msg == WM_DEVICECHANGE)
|
if (msg == WM_DEVICECHANGE)
|
||||||
{
|
{
|
||||||
@@ -125,8 +184,10 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
using (var testHandle = CreateFile(devicePath, 0,
|
using (var testHandle = CreateFile(devicePath, 0,
|
||||||
FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero))
|
FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero))
|
||||||
{
|
{
|
||||||
if (testHandle.IsInvalid)
|
if (testHandle.IsInvalid)
|
||||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
{
|
||||||
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var device = new HidWindows(devicePath);
|
var device = new HidWindows(devicePath);
|
||||||
@@ -188,7 +249,9 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||||
|
|
||||||
if (devInfo == IntPtr.Zero || devInfo == new IntPtr(-1))
|
if (devInfo == IntPtr.Zero || devInfo == new IntPtr(-1))
|
||||||
|
{
|
||||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "SetupDiGetClassDevs failed");
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "SetupDiGetClassDevs failed");
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -203,7 +266,9 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
{
|
{
|
||||||
int error = Marshal.GetLastWin32Error();
|
int error = Marshal.GetLastWin32Error();
|
||||||
if (error == ERROR_NO_MORE_ITEMS)
|
if (error == ERROR_NO_MORE_ITEMS)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
throw new Win32Exception(error, "SetupDiEnumDeviceInterfaces failed");
|
throw new Win32Exception(error, "SetupDiEnumDeviceInterfaces failed");
|
||||||
}
|
}
|
||||||
@@ -211,7 +276,9 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
uint requiredSize = 0;
|
uint requiredSize = 0;
|
||||||
SetupDiGetDeviceInterfaceDetail(devInfo, ref iface, IntPtr.Zero, 0, ref requiredSize, IntPtr.Zero);
|
SetupDiGetDeviceInterfaceDetail(devInfo, ref iface, IntPtr.Zero, 0, ref requiredSize, IntPtr.Zero);
|
||||||
if (Marshal.GetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER || requiredSize == 0)
|
if (Marshal.GetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER || requiredSize == 0)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
IntPtr detailDataBuffer = Marshal.AllocHGlobal((int)requiredSize);
|
IntPtr detailDataBuffer = Marshal.AllocHGlobal((int)requiredSize);
|
||||||
try
|
try
|
||||||
@@ -229,13 +296,17 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
string devicePath = Marshal.PtrToStringAuto(pDevicePathName);
|
string devicePath = Marshal.PtrToStringAuto(pDevicePathName);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(devicePath))
|
if (string.IsNullOrWhiteSpace(devicePath))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Try to open with zero access to ensure it’s reachable
|
// Try to open with zero access to ensure it’s reachable
|
||||||
using (var testHandle = CreateFile(devicePath, 0, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero))
|
using (var testHandle = CreateFile(devicePath, 0, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero))
|
||||||
{
|
{
|
||||||
if (testHandle.IsInvalid)
|
if (testHandle.IsInvalid)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HidWindows device;
|
HidWindows device;
|
||||||
@@ -296,6 +367,35 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
_knownDevices.Clear();
|
_knownDevices.Clear();
|
||||||
|
|
||||||
|
if (_messageThread != null && _messageThread.IsAlive)
|
||||||
|
{
|
||||||
|
// Post a quit message to end the message loop
|
||||||
|
_messageThread.Join();
|
||||||
|
_messageThread = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_windowProcDelegate != null)
|
||||||
|
{
|
||||||
|
_windowProcDelegate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_messageWindowHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DestroyWindow(_messageWindowHandle);
|
||||||
|
_messageWindowHandle = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_deviceNotificationHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
UnregisterDeviceNotification(_deviceNotificationHandle);
|
||||||
|
_deviceNotificationHandle = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( _messageWindowHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DestroyWindow(_messageWindowHandle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,22 +421,21 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
public const int DBT_DEVICEREMOVECOMPLETE = 0x8004;
|
public const int DBT_DEVICEREMOVECOMPLETE = 0x8004;
|
||||||
public const int DBT_DEVTYP_DEVICEINTERFACE = 5;
|
public const int DBT_DEVTYP_DEVICEINTERFACE = 5;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct WNDCLASS
|
public struct WNDCLASS
|
||||||
{
|
{
|
||||||
public uint style;
|
public uint style;
|
||||||
[MarshalAs(UnmanagedType.FunctionPtr)]
|
public WndProc lpfnWndProc;
|
||||||
public WndProc lpfnWndProc;
|
public int cbClsExtra;
|
||||||
public int cbClsExtra;
|
public int cbWndExtra;
|
||||||
public int cbWndExtra;
|
public IntPtr hInstance;
|
||||||
public IntPtr hInstance;
|
public IntPtr hIcon;
|
||||||
public IntPtr hIcon;
|
public IntPtr hCursor;
|
||||||
public IntPtr hCursor;
|
public IntPtr hbrBackground;
|
||||||
public IntPtr hbrBackground;
|
[MarshalAs(UnmanagedType.LPTStr)]
|
||||||
[MarshalAs(UnmanagedType.LPTStr)]
|
public string lpszMenuName;
|
||||||
public string lpszMenuName;
|
[MarshalAs(UnmanagedType.LPTStr)]
|
||||||
[MarshalAs(UnmanagedType.LPTStr)]
|
public string lpszClassName;
|
||||||
public string lpszClassName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
@@ -373,9 +472,20 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
|
||||||
public string DevicePath;
|
public string DevicePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct MSG
|
||||||
|
{
|
||||||
|
public IntPtr hwnd;
|
||||||
|
public uint message;
|
||||||
|
public IntPtr wParam;
|
||||||
|
public IntPtr lParam;
|
||||||
|
public uint time;
|
||||||
|
public int pt_x;
|
||||||
|
public int pt_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Declare delegate for WndProc
|
// Declare delegate for WndProc
|
||||||
public delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
public delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
@@ -397,9 +507,6 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
public static extern bool DestroyWindow(IntPtr hWnd);
|
public static extern bool DestroyWindow(IntPtr hWnd);
|
||||||
|
|
||||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
|
||||||
public static extern IntPtr GetModuleHandle(string lpModuleName);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
public static extern IntPtr DefWindowProc(IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam);
|
public static extern IntPtr DefWindowProc(IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
@@ -454,7 +561,22 @@ namespace EonaCat.HID.Managers.Windows
|
|||||||
IntPtr lpSecurityAttributes,
|
IntPtr lpSecurityAttributes,
|
||||||
int dwCreationDisposition,
|
int dwCreationDisposition,
|
||||||
int dwFlagsAndAttributes,
|
int dwFlagsAndAttributes,
|
||||||
IntPtr hTemplateFile);
|
IntPtr hTemplateFile);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
public static extern bool GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool TranslateMessage([In] ref MSG lpMsg);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern IntPtr DispatchMessage([In] ref MSG lpMsg);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern IntPtr GetModuleHandle(string lpModuleName);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, int Flags);
|
||||||
|
|
||||||
// HID APIs
|
// HID APIs
|
||||||
[DllImport("hid.dll", SetLastError = true)]
|
[DllImport("hid.dll", SetLastError = true)]
|
||||||
|
Reference in New Issue
Block a user