【发布时间】:2020-11-28 17:19:02
【问题描述】:
作为一名飞行模拟爱好者,我正在制作自己的驾驶舱硬件。我使用 USB HID 接口(基于 PIC 微控制器的硬件)将此硬件与我的计算机连接。所有硬件都具有相同的 VID/PID,产品描述符字符串为“FMGS HW Device”(仅供参考:FMGS 是我正在使用的 Airbus A320 模拟器软件的名称)。
在启动时,我的应用程序会扫描所有 USB 设备,并且只将具有我的“VID/PID/产品描述符”组合的设备放入字典中。作为关键,我正在使用通过 PSP_DEVICE_INTERFACE_DETAIL_DATA 结构检索到的“DevicePath”,并调用 SetupDiGetDeviceInterfaceDetail (setupapi.dll)。
下面是一小段代码摘录,展示了此操作的核心 - 只是为了让您明白:
// Retrieving the detailDataBuffer
SetupApi.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, IntPtr.Zero);
// Skip over cbsize (4 bytes) to get the address of the devicePathName
var pDevicePath = new IntPtr(detailDataBuffer.ToInt32() + 4);
// Get the String containing the devicePath
AddDevice(Marshal.PtrToStringAuto(pDevicePath));
此 DevicePath 具有以下格式:
\?\hid#vid_04d8&pid_003f#9&599cfdc&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
问题 1: 我可以清楚地看到 VID/PID 部分 (vid_04d8&pid_003f) 和 HidGuid (4d1e55b2-f16f-11cf-88cb-001111000030),但是“9&599cfdc&0&0000”的部分是什么?每个设备都是独一无二的吗?换句话说,我的 2 台设备具有完全相同的 DevicePath 是否存在风险?
现在我还想检测应用程序运行时设备是否连接/断开。如果设备断开连接,我需要将它们从我的字典中删除。如果设备已连接,我需要将它们放入我的字典并开始与它们通信。
我正在使用“WMI 方法”(我知道 WndProc 中还有一个 WM_DEVICECHANGE 方法,不知道哪个更好?)。以下是代码(尚未完成,但目前有效)。
private void DeviceInsertedEvent(object sender, EventArrivedEventArgs e)
{
Debug.WriteLine($"DeviceInsertEvent");
ManagementBaseObject instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
foreach(PropertyData p in instance.Properties )
{
Debug.WriteLine($"{p.Name} = {p.Value }");
}
Debug.WriteLine($"{instance.Properties["Dependent"].Value }");
}
private void DeviceRemovedEvent(object sender, EventArrivedEventArgs e)
{
Debug.WriteLine($"DeviceRemovedEvent");
ManagementBaseObject instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
foreach (PropertyData p in instance.Properties)
{
Debug.WriteLine($"{p.Name} = {p.Value }");
}
Debug.WriteLine($"{instance.Properties["Dependent"].Value}");
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
var scope = new ManagementScope("root\\CIMV2");
scope.Options.EnablePrivileges = true;
try
{
WqlEventQuery insertQuery = new WqlEventQuery();
insertQuery.EventClassName = "__InstanceCreationEvent";
insertQuery.WithinInterval = new TimeSpan(0, 0, 1);
insertQuery.Condition = @"TargetInstance ISA 'Win32_USBControllerdevice'";
ManagementEventWatcher insertWatcher = new ManagementEventWatcher(insertQuery);
insertWatcher.EventArrived += new EventArrivedEventHandler(DeviceInsertedEvent);
insertWatcher.Start();
WqlEventQuery removeQuery = new WqlEventQuery();
removeQuery.EventClassName = "__InstanceDeletionEvent";
removeQuery.WithinInterval = new TimeSpan(0, 0, 1);
removeQuery.Condition = @"TargetInstance ISA 'Win32_USBControllerdevice'";
ManagementEventWatcher removeWatcher = new ManagementEventWatcher(removeQuery);
removeWatcher.EventArrived += new EventArrivedEventHandler(DeviceRemovedEvent);
removeWatcher.Start();
}
catch (Exception ex)
{
Debug.WriteLine($"backgroundWorker_DoWork Exception: {ex}");
}
while (true) ;
}
我不是 WMI 专家,所以老实说,我不知道自己在做什么(仍在学习)——只是从几个谷歌搜索中获得了代码。我发现 "instance.Properties["Dependent"].Value" 也给了我某种 DevicePath,格式如下。
\DESKTOP-HANS\root\cimv2:Win32_PnPEntity.DeviceID="HID\VID_04D8&PID_003F\9&2E7AE93E&0&0000"
我看到了相同的 VID/PID 组合,以及我在问题 1 中要求的未知“9&2E7AE93E&0&0000”部分。所以基本上,通过一些字符串操作,我可以重建我在字典中用作键的相同 DevicePath。
问题 2: 是否有另一种方法来发现设备连接/断开连接事件,这些事件给我的 DevicePath 与 SetupDiGetDeviceInterfaceDetail 返回的相同?
【问题讨论】:
-
刚刚发现一篇有趣的文章,通过 WM_DEVICECHANGE 详细解释了到达/移除事件。请在此处找到它:codeproject.com/Articles/14500/…。此事件提供了一个结构 DEV_BROADCAST_DEVICEINTERFACE,其成员 dbcc_name 提供与 SetupDiGetDeviceInterfaceDetail 相同格式的 DevicePath。我还没有尝试过这种方法,但肯定会这样做。 WMI 方法有效(见下文),但当我以每毫秒 1 条消息的速度对设备进行压力测试时,它感觉不是很敏感。只是想看看 WM_DEVICECHANGE 是如何工作的。
标签: c# visual-studio usb hid