• DeviceWatcher
  • 编写与 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 设备。
  • 连接具有特定供应商 ID 和产品 ID 的 USB 设备,而无需知道设备接口 GUID。
  • 连接具有已知设备类的 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
  • 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。 该 GUID 是由 UWP 应用Winusb.sys注册的实际设备接口 GUID。

  • 如果知道设备的设备类或其类、子类和协议代码,请调用 GetDeviceClassSelector 来生成 AQS 字符串。

    通过指定 ClassCode SubclassCode ProtocolCode 属性值创建 UsbDeviceClass 对象。 或者,如果知道设备的设备类,可以通过指定特定的 UsbDeviceClasses 属性来调用构造函数。

    查找设备 - 基本方式

    这是查找 USB 设备的最简单方法。 有关详细信息,请参阅 快速入门:枚举常用设备

  • 将检索到的 AQS 字符串传递给 FindAllAsync 。 调用检索 DeviceInformationCollection 对象。
  • 通过集合Loop。 每次迭代都会获取 DeviceInformation 对象。
  • 获取 DeviceInformation.Id 属性值。 字符串值是设备实例路径。 例如,“\\\\?\\USB#VID_045E& PID_078F#61b8ff02605&&&#{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);
                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 对象上的“已添加”和“已删除”事件实现和注册处理程序。 当系统添加或删除具有相同标识信息) 的设备 (时,将调用这些事件处理程序。

  • 启动和停止 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++ 应用必须使用 delete 关键字释放引用。 C#/VB应用必须调用 UsbDevice.Dispose 方法。 JavaScript 应用必须调用 UsbDevice.Close

    如果设备正在使用或找不到, FromIdAsync 将失败。

  •