宿州网站建设公司哪家好,企业微信app下载安装电脑版,上海闵行区邮编,乡村旅游网站的建设国庆节回来后的工作内容#xff0c;基本都在围绕着各种各样的硬件展开#xff0c;这无疑让本就漫长的 “七天班” #xff0c;更加平添了三分枯燥#xff0c;我甚至在不知不觉中学会了#xff0c;如何给打印机装上不同尺寸的纸张。华为的 Mate 60 发布以后#xff0c;人群…国庆节回来后的工作内容基本都在围绕着各种各样的硬件展开这无疑让本就漫长的 “七天班” 更加平添了三分枯燥我甚至在不知不觉中学会了如何给打印机装上不同尺寸的纸张。华为的 Mate 60 发布以后人群中此起彼伏地传出 “遥遥领先” 的声音大概人类总是热衷于评价那些不甚了解的事物。这个现象到了工作中就会变成总有某些人觉得某件事情特别简单。其实。一切你认为“简单”的东西背后一定有无数的人们上下求索、苦心孤诣就像计算机从早期的埃尼阿克(ENIAC)发展到今天的智能手机你能使用它并不代表它就“简单”人还是应该对为止的领域保持敬畏和谦逊。回到这篇文章今天我想和大家聊一聊我为了解决那些“简单”的问题而做出的尝试。本期的故事主角是我们最熟悉不过的 USB 设备有道是 “千古兴亡多少事”且听我娓娓道来。
故事是这样的基于某些不可抗因素上的考虑博主需要在程序中集成某厂商的硬件。我猜测人们觉得这件事情“简单”或许是看到这个设备有一条 USB 连接线因为在人们的固有印象中只要把它接到电脑上就可以正常工作了。事实的确如此因为你只要考虑串口(SerialPort)、USB 以及这两者间的相互转换即可。当然这世上的事情圆满者少遗憾者多博主在使用过程中发现厂商的提供的 SDK 存在 Bug当设备从电脑上拔出后其 SDK 的初始化函数依然正常返回了这意味着我们无法在使用设备前“正确”地检测出硬件状态。考虑厂商愿不愿意修复这个 Bug 还是个未知数博主不得不尝试另辟蹊径。 相信这张图片大家都见过无数次啦在这里你可以看到操作系统接入的各种设备。以鼠标为例通过下面这个对话框我们可以获得这个设备的各种属性信息 在各种属性信息中硬件 Id 是最为关键的一组信息我们可以看到鼠标这个设备的 VID 为 0000PID 为 3825。其中VID 是指 Vender ID即供应商识别码PID 是指 Product ID即产品识别码。事实上所有的 USB 设备都有 VID 和 PIDVID 由供应商向 USB-IF 申请获得而 PID 则由供应商自行指定计算机正是 VID、PID 以及设备的版本号来决定加载或者安装相应的驱动程序。因此如果想要判断计算机是否连接了某个 USB 设备我们可以使用下面的方案
bool HasUsbDevice(string vid, string pid)
{var query $SELECT * FROM Win32_PnPEntity WHERE DeviceID LIKE USB%VID_{vid}PID_{pid}%;var searcher new ManagementObjectSearcher(query);var devices searcher.Get();return devices.Count 0;
}需要说明的是这是通过古老的 WMI 来查询 USB 设备信息还记得我们前面收集到的 VID 以及 PID 吗此时我们需要简单调用一下即可
if (HasUsbDevice2(0000, 3825) {Console.WriteLine([WMI]设备已连接);
} else {Console.WriteLine([WMI]设备未连接);
}当然在 .NET 8.0 发布以后依然固执地抱着这些 Windows 平台的 API 不放多少有点食古不化的意味。所以实际工作中我会推荐本文题目中的 LibUsbDotNet 库除了跨平台方面的考量这个库的功能要更强大一点可以做到向 USB 设备发送数据或者从 USB 设备接收数据。下面由我来对这个库的使用进行说明目前我们可以从 Github 以及 SourceForge 上下载对应的项目两者的区别是 Github 上的项目更新一点
https://github.com/LibUsbDotNet/LibUsbDotNethttps://sourceforge.net/projects/libusbdotnet/
下载后是一个可执行文件我们点击安装即可它会安装好相关的库以及驱动文件默认的安装目录为C:\Program Files\LibUsbDotNet。在安装完成后它会提示我们进入下面的对话框这一步的目的是给特定的设备安装 libusb 驱动因为只有安装了驱动的情况下接下来的一切才会发生除非 LibUsbDotNet 会隔空取物。 这里我们还是选择鼠标这个硬件你需要重点关注 PID 以及 VID 两个参数因为这是唯一能区分不同 USB 设备的标识 最后点击 “Install” 按钮即可为当前设备安装 libusb 驱动。接下来的事情就变得非常简单啦我们只需要通过 NuGet 安装 LibUsbDotNet 即可
bool HasUsbDevice(short vid, short pid)
{var useDeviceFinder new UsbDeviceFinder(vid, pid);var usbDevice UsbDevice.OpenUsbDevice(useDeviceFinder);return usbDevice ! null;
}可以注意到LibUsbDotNet 需要的 VID 以及 PID 都是 short 类型的所以相比于 WMI 的方案它在调用上会存在一点差异
var verdorId Convert.ToInt16(0x0000, 16);
var productId Convert.ToInt16(0x3825, 16);
if (HasUsbDevice(verdorId, productId)) {Console.WriteLine([LibUsbDotNet]设备已连接);
} else {Console.WriteLine([LibUsbDotNet]设备未连接);
}显然你会注意到我在原本的 “0000” 和 “3825” 前面都补了 “0x” 这样的字符这是因为 VID 和 PID 都是 16 位的二进制数它们都可以简写为 4 位十六进制数所以不管是在 Windows 上还是 LibUsbDotNet 提供的软件中它都是以 4 位十六进制数的简写形式存在的。因此这里就需要进行先补 “0x” 再做转换的处理。 除了判断 USB 设备是否存在有时候我们还需要关注 USB 设备的状态变化。例如插入 USB 设备或者拔出 USB 设备。古人云世上本无事庸人自扰之。可这个世界上还就真的有这般无聊的人动辄喜欢搞拔设备、拔网线这种所谓的深度测试。所以下面我们来考虑如何处理这种极端的场景。从一开始博主选择 LibUsbDotNet 这个库就是看到它提供了 DeviceNotifier 这个类型。不过在博主后续的尝试中发现截止到 2.2.29 版本这个类型已然无迹可寻而 3.X 版本目前依然出于预发行状态并且 API 与现在的版本不兼容所以这个念头不得不就此作罢。 当然在觉醒了 WMI 的远古记忆以后我们会意识到 Windows 下存在着一个大型的数据库理论上我们只需要查询这个数据库就可以监听到 USB 设备的状态变化。如图所示我们会注意到每个硬件的对话框里有一个 “事件” 选项卡而这些事件最终会在事件查看器里面汇合。在 ChatGPT 以及 wbemtest 的帮助下我们找到了两个重要的重要的类名__InstanceCreationEvent、__InstanceDeletionEvent。此时我们可以编写出下面的代码
void MonitorUsbDevice()
{// 监听 USB 设备插入var queryInsert new WqlEventQuery(SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA Win32_USBControllerDevice);var watcherInsert new ManagementEventWatcher(queryInsert);watcherInsert.EventArrived (sender, e) {// 被插入的逻辑处理var targetInstance (ManagementBaseObject)e.NewEvent[TargetInstance];// \\SNOWFLY-PC\root\cimv2:Win32_PnPEntity.DeviceIDHID\\VID_0000PID_3825\\62BE8ADFA00000var deviceId targetInstance.Properties[Dependent].Value.ToString();var device new ManagementObject(deviceId);var args new DeviceNotifierEventArgs();// Win32_PnPEntity.DeviceIDHID\\VID_0000PID_3825\\62BE8ADFA00000args.DeviceId device.Path.RelativePath.Split()[1].Replace(\, );args.DevicePath device.Path.ToString();args.Pid 0x deviceId.Split(new char[] { , \\ }).FirstOrDefault(x x.StartsWith(PID_)).Replace(PID_, );args.Vid 0x deviceId.Split(new char[] { , \\ }).FirstOrDefault(x x.StartsWith(VID_)).Replace(VID_, );if (!args.DeviceId.StartsWith(USB)) return;Console.WriteLine($设备已插入 {JsonConvert.SerializeObject(args)});};watcherInsert.Start();var queryDelete new WqlEventQuery(SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA Win32_USBControllerDevice);var watcherDelete new ManagementEventWatcher(queryDelete);watcherDelete.EventArrived (sender, e) {// 被拔出的逻辑处理var targetInstance (ManagementBaseObject)e.NewEvent[TargetInstance];// \\SNOWFLY-PC\root\cimv2:Win32_PnPEntity.DeviceIDHID\\VID_0000PID_3825\\62BE8ADFA00000var deviceId targetInstance.Properties[Dependent].Value.ToString();var device new ManagementObject(deviceId);var args new DeviceNotifierEventArgs();// Win32_PnPEntity.DeviceIDHID\\VID_0000PID_3825\\62BE8ADFA00000args.DeviceId device.Path.RelativePath.Split()[1].Replace(\, );args.DevicePath device.Path.ToString();args.Pid 0x deviceId.Split(new char[] { , \\ }).FirstOrDefault(x x.StartsWith(PID_)).Replace(PID_, );args.Vid 0x deviceId.Split(new char[] { , \\ }).FirstOrDefault(x x.StartsWith(VID_)).Replace(VID_, );if (!args.DeviceId.StartsWith(USB)) return;Console.WriteLine($设备已拔出 {JsonConvert.SerializeObject(args)});};watcherDelete.Start();
}理解这段代码基本上没有任何难度唯一需要说明的地方是插入或者拔出一个 USB 设备实际上会产生两条消息它们分别表示的是设备实例与接口实例的创建。这个话听起来或许有些晦涩可能连微软都不知道它自己在说什么。具体到博主的这个示例中其规律是两者的 DeviceID 格式不同一次是 HID 一个是 USB因此我们只需要过滤掉 HID 的那条消息即可。最终博主实现的效果如下图所示 有了这个思路我们就可以在程序启动时对 USB 设备进行监控一旦发现某个重要的设备被移除程序就可以及时地做出响应或处理而不用等到真正要用设备的时候引发异常我越来越觉得编程本质就是一群聪明人在千方百计地照顾一个“巨婴”每次测试同事都说这里或者那里要加一个提示可即使增加了提示人们依然无止无休地问你为什么错误信息不过是程序员自我安慰剂除了程序员以外没有人会在乎它具体是什么。如果你对此怀疑表示怀疑的话不妨回去翻翻你写的代码有多少行是真正的、有用的代码又有多少代码是为了防呆呢好了以上就是这篇博客的全部内容啦本文完。