如何连接到 USB 设备(UWP 应用)
在 Windows 中,你可以编写与 USB 设备交互的 UWP 应用。
本文介绍:
- 如何使用 DeviceWatcher 对象检测设备
- 如何打开设备进行通信
- 使用完设备后如何关闭设备
重要的 API
当你编写与 USB 设备交互的 UWP 应用时,该应用可以发送控制命令、获取设备信息,以及向/从批量和中断终结点读取和写入数据。 在完成所有这些操作之前,必须找到设备并建立连接。
准备工作
- 这是系列中的第一个主题。 在开始本教程之前,必须已创建一个可在本教程中扩展的基本 Visual Studio 项目。 有关详细信息 ,请阅读 UWP 应用入门 。
- 代码示例基于 CustomUsbDeviceAccess 示例。 可以从此代码库页下载完整示例。
- 教程中使用的 USB 设备是 SuperMUTT 设备。
- 若要使用 Windows.Devices.Usb 命名空间编写与 USB 设备交互的 Windows 应用,设备必须加载 Winusb.sys 驱动程序作为其函数驱动程序。 Winusb.sys 由 Microsoft 提供,包含在 \Windows\System32\drivers 文件夹中的 Windows 中。
流程图:查找设备
若要连接到 USB 设备,必须先根据各种发现模式查找设备,然后连接到该设备:
- 使用特定设备接口 GUID 连接到任何 USB 设备。
- 连接到具有特定供应商 ID 和产品 ID 且具有特定设备接口 GUID 的 USB 设备。
- 在不知道设备接口 GUID 的情况下,使用特定的供应商 ID 和产品 ID 连接到 USB 设备。
- 连接到具有已知设备类的 USB 设备。
关键概念
什么是设备接口 GUID?
内核模型驱动程序在初始化期间注册并公开名为 设备接口 GUID 的 GUID。 通常,应用使用公开的 GUID 查找关联的驱动程序及其设备,然后打开设备的句柄。 检索到的句柄用于后续的读取和写入操作。
但是,在 Winusb.sys 的情况下,可以通过以下两种方式之一提供它,而不是公开设备接口 GUID 的驱动程序:
- 在设备的 MS OS 描述符中。 设备制造商在设备的扩展属性描述符中将 DeviceInterfaceGUID 设置为自定义属性。 有关详细信息,请参阅 Microsoft OS 描述符中的“扩展属性 描述符”文档。
- 如果通过自定义 INF 手动安装 Winusb.sys,则 INF 在 INF 中注册了 GUID。 请参阅 WinUSB (Winusb.sys) 安装。
如果找到了设备的设备接口 GUID,则 UWP 应用可以找到与该设备接口 GUID 匹配的所有设备。
USB 设备标识如何显示在 Windows 中?
每个 USB 设备必须有两条信息:供应商 ID 和产品 ID。
USB-IF 分配这些标识符,设备制造商必须在设备中公开这些标识符。 那么,如何获取该信息呢?
即使设备未加载设备驱动程序,即 Windows 将其检测为“未知设备”,你仍然可以在硬件 ID 属性值中查看设备管理器标识符。 该值是这两个标识符的组合。 例如,对于 SuperMUTT 设备, 硬件 ID 为“USB\VID_045E&PID_F001”;供应商 ID 为“0x045E”,产品 ID 为“0xF001”。
如果设备有 INF,请从 “模型” 部分获取该字符串。
可以检查各种注册表设置。 最简单的方法是查看
<HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Enum\USB\硬件 ID>
有关详细信息,请参阅 USB 设备注册表项。
应用清单使用硬件 ID 来标识设备。
<Device Id=“vidpid:045e f001”>
UWP 应用可以找到与特定供应商和产品 ID 匹配的所有设备。 可以通过指定设备接口 GUID 来缩小搜索结果范围。
什么是 USB 设备类?
大多数 USB 设备都符合 USB-IF 批准的设备类规范。 通过使用这些规范,具有相似性质的设备可以以标准方式展示其功能。 此方法的最大优点是设备可以使用 Microsoft 提供的内置类驱动程序或通用 Winusb.sys 驱动程序。
某些设备可能不符合 USB-IF 规范。 而是公开 供应商定义的 功能。 对于此类设备,供应商必须提供设备驱动程序,或者可以使用 Winusb.sys。
无论设备是供应商定义的还是符合设备类,它都必须描述此设备类的相关信息:
- 类代码:指示设备所属的设备类。
- 子类代码:在设备类中,指示设备的子类。
- 协议代码:设备使用的协议。
例如,SuperMUTT 设备是供应商定义的设备,该信息由类代码指示为 FF。 如果设备将类代码显示为 FEh,将子类代码显示为 02h,将协议代码显示为 00h,则可以断定设备是符合类标准的 IrDA 桥设备。 UWP 应用可以与属于以下设备类的设备通信:
- ActiveSync
- CdcControl
- DeviceFirmwareUpdate
- IrDA
- 度量
- PalmSync
- PersonalHealthcare
- 物理
- VendorSpecific
UWP 应用可以找到与一组特定类、子类和协议代码匹配的所有设备。
获取设备的 AQS) 字符串 (高级查询语法
(AQS) 生成高级查询字符串,其中包含要检测的设备的标识信息。 可以通过指定供应商/产品 ID、设备接口 GUID 或设备类来生成字符串。
如果要提供供应商 ID/产品 ID 或设备接口 GUID,请调用 GetDeviceSelector 的任何重载。
在 SuperMUTT 设备的示例中, GetDeviceSelector 检索类似于以下字符串的 AQS 字符串:
"System.Devices.InterfaceClassGuid:="{DEE824EF-729B-4A0E-9C14-B7117D33A817}" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True AND System.DeviceInterface.WinUsb.UsbVendorId:=1118 AND System.DeviceInterface.WinUsb.UsbProductId:=61441"
注意 请注意,字符串中显示的设备接口 GUID 不是指定的。 该 GUID 是由 Winusb.sys 为 UWP 应用注册的实际设备接口 GUID。
如果知道设备的设备类或其类、子类和协议代码,请调用 GetDeviceClassSelector 以生成 AQS 字符串。
通过指定 ClassCode、SubclassCode 和 ProtocolCode 属性值创建 UsbDeviceClass 对象。 或者,如果知道设备的设备类,可以通过指定特定的 UsbDeviceClasses 属性来调用构造函数。
查找设备 - 基本方法
这是查找 USB 设备的最简单方法。 有关详细信息,请参阅 快速入门:枚举常用设备。
- 将检索到的 AQS 字符串传递给 FindAllAsync。 调用检索 DeviceInformationCollection 对象。
- 循环访问集合。 每次迭代都会获取 一个 DeviceInformation 对象。
- 获取 DeviceInformation.Id 属性值。 字符串值是设备实例路径。 例如,“\\\?\USB#VID_045E&PID_078F#6&1b8ff026&0&5#{dee824ef-729b-4a0e-9c14-b7117d33a817}”。
- 通过传递设备实例字符串并获取 UsbDevice 对象来调用 FromIdAsync。 然后,可以使用 UsbDevice 对象执行其他操作,例如发送控制传输。 应用使用 完 UsbDevice 对象后,应用必须通过调用 Close 释放它。 注意 当 UWP 应用暂停时,设备会自动关闭。 为了避免在将来的操作中使用过时的句柄,应用必须发布 UsbDevice 参考。
private async void OpenDevice()
{
UInt32 vid = 0x045E;
UInt32 pid = 0x0611;
string aqs = UsbDevice.GetDeviceSelector(vid, pid);
var myDevices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(aqs);
try
{
usbDevice = await UsbDevice.FromIdAsync(myDevices[0].Id);
}
catch (Exception exception)
{
ShowStatus(exception.Message.ToString());
}
finally
{
ShowStatus("Opened device for communication.");
}
}
查找设备 - 使用 DeviceWatcher
或者,可以动态枚举设备。 然后,如果添加或删除设备,或者设备属性发生更改,应用可以接收通知。 有关详细信息,请参阅 在添加、删除或更改设备时如何获取通知。
DeviceWatcher 对象使应用能够在从系统中添加和删除设备时动态检测设备。
创建 DeviceWatcher 对象,以检测何时向系统添加或删除设备。 必须通过调用 CreateWatcher 并指定 AQS 字符串来创建对象。
在 DeviceWatcher 对象上实现和注册 Added 和 Removed 事件的处理程序。 当系统添加或删除具有相同标识信息) 的设备 (时,将调用这些事件处理程序。
启动和停止 DeviceWatcher 对象。
应用必须通过调用 Start 来启动 DeviceWatcher 对象,以便它可以在系统添加或删除设备时开始检测设备。 相反,当不再需要检测设备时,应用必须通过调用 Stop 来停止 DeviceWatcher。 此示例有两个按钮,允许用户启动和停止 DeviceWatcher。
此代码示例演示如何创建和启动设备观察程序来查找 SuperMUTT 设备的实例。
void CreateSuperMuttDeviceWatcher(void)
{
UInt32 vid = 0x045E;
UInt32 pid = 0x0611;
string aqs = UsbDevice.GetDeviceSelector(vid, pid);
var superMuttWatcher = DeviceInformation.CreateWatcher(aqs);
superMuttWatcher.Added += new TypedEventHandler<DeviceWatcher, DeviceInformation>
(this.OnDeviceAdded);
superMuttWatcher.Removed += new TypedEventHandler<DeviceWatcher, DeviceInformationUpdate>
(this.OnDeviceRemoved);
superMuttWatcher.Start();
}
打开设备
若要打开设备,应用必须通过调用静态方法 FromIdAsync 并 传递从 DeviceInformation.Id) 获取的设备实例路径 (来启动异步操作。 该操作获取的结果是 UsbDevice 对象,该对象用于与设备的未来通信,例如执行数据传输。
使用 完 UsbDevice 对象后,必须释放它。 通过释放 对象,将取消所有挂起的数据传输。 这些操作的完成回调例程仍会调用并出现取消错误或操作已完成。
C++ 应用必须使用删除关键字 (keyword) 释放引用。 C#/VB 应用必须调用 UsbDevice.Dispose 方法。 JavaScript 应用必须调用 UsbDevice.Close。
如果设备正在使用或找不到, FromIdAsync 将失败。