将集与 DeviceInformationPairing.Custom 属性配对

注意

一些信息与预发布产品相关,在商业发行之前可能发生实质性修改。 Microsoft 对此处提供的信息不提供任何明示或暗示的保证。

使用 Windows.Devices.Enumeration API 将设备配对为一个集。

Windows 支持将设备集合配对为一个集。 该平台支持两种类型的集。

  • 自动发现的集。 当协议(如蓝牙 LE)在配对主终结点时自动发现属于该集的其他终结点时,就会发生这种情况。 例如,当用户通过蓝牙 LE 配对右耳塞时,协议堆栈可能会发现左耳塞;因此,两者可以一同配对为一个集。
  • 显式集。 当通过多个协议发现设备时,这些功能非常有用。 例如,通常通过三种协议发现 Internet 打印协议 (IPP) 打印机:IPP、WSD 和 eSCL。 发现同一设备的多个终结点时,可以将这些终结点一同显式配对为一个集。

自动发现的集(蓝牙样式)的代码示例

此代码示例使用自定义配对对象实施自动发现的(蓝牙样式)集配对。 与典型的自定义配对实施一样,需要配对请求的处理程序才能处理配对仪式。 在这种情况下,代码示例仅实施仅确认配对仪式。 相关新增功能是添加 pairing-set-member-requested 处理程序。

set-member-handler 使平台能够尝试将传入的设备配对为一个集。 如果没有该处理程序,协议堆栈不会尝试枚举配对集的成员。 蓝牙协议将发现集成员作为完成配对的一部分,因此有时会在仪式处理程序返回后调用集处理程序。 在此流中,集处理程序通常会在处理具有固定集成员列表的仪式后调用一次;这意味着协议可以在配对期间发现所有终结点,并且以后不会发现更多终结点。

此代码示例会将所有发现的集成员与用于配对主设备/终结点的相同 BtSetPairing 例程同步配对。 还支持并行配对,并且可能对您的方案更有效。 但为简单起见,不会在代码示例中显示。 由于我们还会将集成员与集处理程序配对,因此它们可能会以递归方式生成要配对的更多集成员。 但是通常,发现的集成员的集处理程序可能只会看到发现已完成,并且集成员向量为空。

注意

此代码示例没有大型方案或应用的上下文;但应用可能需要跟踪其配对的设备以及配对结果。 这是为了让应用了解整个集配对操作是否成功。

注意

这些 API 通常为异步。 配对操作有自己的工作线程,并在不同的线程上调用处理程序。 代码不必像此代码示例那样频繁地阻止操作。

C++/WinRT 中的代码示例

void PairingTests::BtSetPairing(DeviceInformation device)
{
    DevicePairingKinds ceremonies = DevicePairingKinds::ConfirmOnly;
    auto customPairing = device.Pairing().Custom();
    event_revoker ceremonyEventToken = customPairing.PairingRequested(
        { this, &PairingTests::PairingRequestedHandler });
    event_revoker setEventToken = customPairing.PairingSetMembersRequested(
        { this, &PairingTests::PairingSetMembersRequestedHandler });

    DevicePairingResult result = customPairing.PairAsync(ceremonies).get();

    if (DevicePairingResultStatus::Paired == result.Status()) // Pairing worked.
    else // Handle pairing failure.
}

void PairingTests::PairingRequestedHandler(DeviceInformationCustomPairing const&,
    DevicePairingRequestedEventArgs const& args)
{
    switch (args.PairingKind())
    {
    case DevicePairingKinds::ConfirmOnly:
        args.Accept();
        break;
    default:
        // This code example doesn't implement other ceremonies.
        // The handler wouldn't be called for ceremonies that the app didn't register.
    }
}

void PairingTests::PairingSetMembersRequestedHandler(DeviceInformationCustomPairing
    const&, DevicePairingSetMembersRequestedEventArgs const& args)
{
    switch (args.Status())
    {
    case DevicePairingAddPairingSetMemberStatus::SetDiscoveryCompletedByProtocol:
        // This is the expected result if we started set pairing 
        // a Bluetooth device. Note: there still might be no set members.
        break;
    case DevicePairingAddPairingSetMemberStatus::SetDiscoveryPartiallyCompletedByProtocol:
        // The protocol enumerated some but not all set members.
        break;
    case DevicePairingAddPairingSetMemberStatus::SetDiscoveryNotAttemptedByProtocol:
        // Not expected for Bluetooth.
        // This constant implies that the protocol likely doesn't support set-pairing.
    default:
        // The other constants aren't expected in an auto-discovered Bluetooth set scenario.
        // Error handling can go here.
    }

    for (auto setMember : args.PairingSetMembers())
    {
        BtSetPairing(setMember);
    }
}

显式集(IPP 样式)的代码示例

此代码示例使用自定义配对对象实施显式集配对。 与典型的自定义配对实施一样,需要配对请求的处理程序才能处理配对仪式。 在这种情况下,代码示例仅实施仅确认配对仪式。 与蓝牙代码示例一样,相关新功能是添加 pairing-set-member-requested 处理程序。 集成员处理程序使平台能够尝试将设备配对为一个集。

与蓝牙样式集配对方案相比,此代码示例显式将设备添加到集。 与蓝牙相比,集处理程序意味着配对 IPP 打印机相关的协议略有不同。 这意味着客户端正在通过各种协议处理设备发现,并且应同步因配对所有集成员而创建的 PnP 状态和打印队列。

为了确保简单的代码示例实施过程,假定事先已发现集成员终结点的向量,并作为参数与主设备一起传入。 例如,在典型的 IPP 方案中,按任意顺序发现终结点。 因此,例如可以通过 WSD 发现主设备;然后,向量将包含表示通过 IPP 和 eSCL 发现的终结点的设备。 但是,任何组合都可行且有效。 它将集成员添加到应用主线程上主设备的自定义配对对象,然后调用 PairAsync

注意

实际上,可以随时在任何线程上将集成员添加到自定义配对对象。 通过协议执行的操作可能需要很长时间,甚至可能会被阻止,直到超时,因此重叠操作非常有用。 请考虑利用 API 的并行性来同时添加和配对设备。 即使仍在通过网络枚举设备也是如此。 将它们配对为一个集的优点仍然普遍适用。

通过此实施,主集成员将与集成员同时配对。 集成员在处理程序中一次同步配对一个。 但同样,它们可以并行配对,以提高效率。

PnP 中的设备节点对象通常因配对而创建。 对于 IPP,在配对后始终为每个终结点创建设备节点。 此集配对 API 在集中的终结点之间隐式同步设备节点创建。 在此代码示例的流中,所有设备节点都将同步,因为在配对开始之前会添加所有集成员。 有关此 API 如何在 PnP 中同步设备节点的更多详细信息,请参阅本主题中的一般评论部分。

注意

此代码示例没有大型方案或应用的上下文;但应用可能需要跟踪其配对的设备以及配对结果。 这是为了让应用了解整个集配对操作是否成功。

C++/WinRT 中的代码示例

void PairingTests::IppSetPairing(DeviceInformation device,
    std::vector<DeviceInformation> const& setMemberDevices)
{
    DevicePairingKinds ceremonies = DevicePairingKinds::ConfirmOnly;
    auto customPairing = device.Pairing().Custom();
    event_revoker ceremonyEventToken = customPairing.PairingRequested({ this,
                     &PairingTests::PairingRequestedHandler });
    event_revoker setEventToken = customPairing.PairingSetMembersRequested({ this,
                  &PairingTests::PairingSetMembersRequestedHandler });

    if (setMemberDevices)
    {
        for (auto setDevice : setMemberDevices)
        {
            customPairing.AddPairingSetMember(setDevice);
        }
    }

    DevicePairingResult result = customPairing.PairAsync(ceremonies).get();

    if (DevicePairingResultStatus::Paired == result.Status()) // Pairing worked.
    else // Handle pairing failure.
}

void PairingTests::PairingRequestedHandler(DeviceInformationCustomPairing const&,
    DevicePairingRequestedEventArgs const& args)
{
    switch (args.PairingKind())
    {
    case DevicePairingKinds::ConfirmOnly:
        args.Accept();
        break;
    }
}

void PairingTests::PairingSetMembersRequestedHandler(DeviceInformationCustomPairing const&,
    DevicePairingSetMembersRequestedEventArgs args)
{
    switch (args.Status())
    {
    case DevicePairingAddPairingSetMemberStatus::AddedToSet:
        // This is the expected result of adding a set member(s) to
        // a Bluetooth device. Note: there still might be no set members.
        break;
    case DevicePairingAddPairingSetMemberStatus::CouldNotBeAddedToSet:
        // Means we failed to add set member(s).
        break;
    case DevicePairingAddPairingSetMemberStatus::SetDiscoveryNotAttemptedByProtocol:
    default:
        // The other constants aren't expected in an auto-discovered Bluetooth set scenario.
        // Error handling can go here.
    }

    for (auto setMember : args.PairingSetMembers())
    {
        IppSetPairing(setMember, nullptr);
    }
}

一般评论

自动发现的(蓝牙样式)集配对

当协议在配对主终结点后发现集成员时,必须使用此 API 才能完成蓝牙样式集配对。 一个简单的示例可能是一组无线耳塞。 当第一个耳塞配对完成时,设备会通知电脑将第二台设备配对为集的一部分。 扩展自定义配对 API 后,应用能够通过新的添加集成员状态处理程序处理集操作。

显式(IPP 样式)集配对

同样,还可以使用此 API 将任何一组关联终结点 (AEP) 设备配对为一个集。 使用自定义配对对象,应用可以随时在任何线程上将其他终结点添加到配对集。 这是默认设置,由于每个设备可能需要很长时间才能通过网络实现设备发现和配对,因此我们不希望在可以避免的情况下序列化这些操作。

当通过许多协议发现协议和设备堆栈不容易协调的设备时,在集中配对尤其有用。 例如,Windows 可能会发现具有三种不同的网络协议的新式网络打印机,每个协议都会生成关联终结点。 在这种情况下,将所有三个终结点作为一个集配对非常有用,从而避免通过网络进行浪费资源的重新发现,并创建一个简化的打印队列。

即使网络打印机未配对为一个集,打印后台处理程序仍会尝试为每个用户创建一个打印队列,而不考虑是否可以通过多个协议进行配对。 如果打印机最初通过一个协议配对,则操作系统 (OS) 将尝试通过其他受支持的协议重新发现打印机,并关联所有协议,以避免重复的打印队列。 通常,OS 可以快速且成功地执行该操作,并生成一个简化的打印队列。

但是,如果应用已发现打印机的所有终结点,则此重新发现步骤会浪费资源。 更糟的是,在打印机准备好使用之前会增加较长的延迟时间。 此外,如果协议过于不同步或延迟,则后台处理程序可能必须为同一打印机创建额外的打印队列,这会令最终用户感到困惑。

将所有终结点一次性配对为一个集可以避免重新发现速度缓慢。 它确保同步 PnP 状态,并生成简化的最佳打印队列。

设备节点同步

当使用此 API 将设备配对为一个集时,生成的 PnP 设备状态将方便地实现同步。 API 不会限制何时应用可以添加集成员;但平台何时可以同步设备节点存在限制。 设备节点同步会阻止操作,直到集中的所有终结点完成配对。 之后,将同时创建所有终结点的所有设备节点。 可以在该点后向集中添加更多集成员,但不会阻止后续设备节点创建,而是立即创建节点。

  • 设备节点创建的同步时间
    • 在配对开始之前添加集成员。
    • 在至少一个集成员未完成时添加集成员。
  • 设备节点创建同步:
    • 所有添加的集成员都最终确定之后的任何时间。

实际上,API 不允许应用控制仪式最终确定完成的时间,以影响设备节点同步的行为方式。 最接近的近似值将是应用选择完成仪式的时间。 在应用仪式处理程序返回之前,配对无法完成;因此,这是应用影响最终确定所有集成员的时间的最后一次机会。