将数据集与 DeviceInformationPairing.Custom 属性配对

注释

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

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

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

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

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

此代码示例通过自定义配对对象实现蓝牙样式的自动发现组配对。 与自定义配对的典型实现一样,需要 配对请求的处理程序 来处理配对仪式。 在这种情况下,代码示例仅实现 仅确认 配对仪式。 新的和有趣的部分是添加一个 pairing-set-member-requested 处理程序。

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

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

注释

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

注释

这些 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 样式)的代码示例

此代码示例使用自定义配对对象实现显式集配对。 与自定义配对的典型实现一样,需要 配对请求的处理程序 来处理配对仪式。 在这种情况下,代码示例仅实现 仅确认 配对仪式。 与蓝牙代码示例类似,有趣的新部分是添加一个对集合成员请求的处理程序。 设置成组成员处理程序使平台能够尝试将设备配对为一组。

与蓝牙样式集配对方案相比,此代码示例显式将设备添加到集。 设置处理程序意味着与配对 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)
        // by calling the AddPairingSetMember method.
        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 explicit set scenario.
        // Error handling can go here.
    }

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

常规评论

自动发现(蓝牙样式)设备配对

此 API 是完成蓝牙风格的集合配对所必不可少的,其中组成员在配对主要端点后由协议发现。 一个简单的示例可能是一组无线耳塞。 当第一个耳塞的配对完成时,设备会通知电脑还有第二个耳塞需要配对以完成整套设备。 扩展自定义配对 API,允许应用通过新的 添加集成员状态 处理程序来处理集合操作。

显式(IPP 样式)集合配对

同样,还可以使用此 API 将任何一组关联终结点(AEP)设备配对为一个集。 使用自定义配对对象,应用可以随时在任何线程上将其他终结点添加到配对集。 这是刻意设计的,因为通过网络进行的设备发现和配对可能会为每个设备花费很长时间,所以我们不想在可以避免的情况下将这些操作进行序列化。

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

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

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

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

设备节点同步

当设备作为集与此 API 配对时,生成的 PnP 设备状态将方便同步。 API 不会限制应用何时可以添加集合成员;但平台在何时能够同步设备节点上存在限制。 设备节点同步会阻止,直到集中的所有终结点完成配对。 之后,所有终结点的设备节点将被同时创建。 此后可以向集合添加更多成员,但是不会阻止后续的设备节点创建,相反,新的节点会立即被创建。

  • 设备节点创建在以下情况同步进行:
    • 集合成员在配对开始前添加。
    • 当至少一个集成员尚未完成时,会添加新的集成员。
  • 设备节点创建 同步:
    • 所有添加的集成员最终确定后的任何时间。

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