如何连接到 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 设备。

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 设备的最简单方法。 有关详细信息,请参阅 快速入门:枚举常用设备

  1. 将检索到的 AQS 字符串传递给 FindAllAsync。 调用检索 DeviceInformationCollection 对象。
  2. 循环访问集合。 每次迭代都会获取 一个 DeviceInformation 对象。
  3. 获取 DeviceInformation.Id 属性值。 字符串值是设备实例路径。 例如,“\\\?\USB#VID_045E&PID_078F#6&1b8ff026&0&5#{dee824ef-729b-4a0e-9c14-b7117d33a817}”。
  4. 通过传递设备实例字符串并获取 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 对象使应用能够在从系统中添加和删除设备时动态检测设备。

  1. 创建 DeviceWatcher 对象,以检测何时向系统添加或删除设备。 必须通过调用 CreateWatcher 并指定 AQS 字符串来创建对象。

  2. DeviceWatcher 对象上实现和注册 AddedRemoved 事件的处理程序。 当系统添加或删除具有相同标识信息) 的设备 (时,将调用这些事件处理程序。

  3. 启动和停止 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 将失败。