BluetoothLEDevice.FromBluetoothAddressAsync() 为何仍返回已移除设备?
结论
BluetoothLEDevice.FromBluetoothAddressAsync() 可以根据 Windows 已知的设备信息返回对象。返回非 null 并不表示设备当前开机、已连接或仍然配对。
Microsoft 文档说明,只有设备既未配对、又不在系统缓存中时,该方法才会因找不到设备而返回 null。单独创建 BluetoothLEDevice 对象也不一定会发起连接。因此,取消配对并关闭设备后仍能获得对象,符合该 API 的行为,不能据此认定设备移除失败。FromBluetoothAddressAsync 文档
关于彻底清除所有设备信息,这些 API 的公开文档没有提供能保证完成这一目标的统一清理接口,也没有给出需要删除的完整注册表清单。
设备信息从哪里来?
该方法可以利用配对信息或系统缓存来解析蓝牙地址。即使设备没有配对,BluetoothLEAdvertisementWatcher 接收到的广播以及设备枚举 API 的扫描,也可以让该设备出现在系统缓存中。设备查找与缓存说明
从目前的现象可以看出,返回对象不依赖设备此刻在线。但仅凭这些操作,无法确定信息具体保存在何处,也无法确定是已有记录仍然存在,还是扫描曾重新填充缓存。
还需要区分两类信息:
- 用于识别设备的系统记录,例如地址和设备身份信息。
- GATT 属性缓存,例如服务、特征和描述符。
BluetoothCacheMode 文档描述的是系统范围内的 GATT 属性缓存。设备取消配对或报告服务变化时,相关缓存条目会失效,但这不表示 Windows 会忘记设备的所有身份信息,也不表示 FromBluetoothAddressAsync() 随后一定返回 null。BluetoothCacheMode 文档
如何检查设备是否真的能够通信?
要判断设备是否可用,可以执行需要实际通信的操作并检查结果,例如:
await device.GetGattServicesAsync(BluetoothCacheMode.Uncached);
Uncached 要求从设备获取数据,未缓存的服务发现可以触发连接尝试。它控制本次查询的数据来源,并不会删除全部设备记录。缓存模式说明
下面的示例适用于已经配置好 WinRT Bluetooth API 支持的 C# 项目:
using System.Threading.Tasks;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
public static async Task<GattCommunicationStatus?> ProbeDeviceAsync(
ulong address)
{
using (var device =
await BluetoothLEDevice.FromBluetoothAddressAsync(address))
{
if (device == null)
{
return null;
}
var result = await device.GetGattServicesAsync(
BluetoothCacheMode.Uncached);
foreach (var service in result.Services)
{
service.Dispose();
}
return result.Status;
}
}
结果的含义分别是:
null:没有获得设备对象。Success:此次服务发现成功,不保证后续操作持续成功。Unreachable:当前无法与设备通信,不能仅据此断定设备已关机。AccessDenied:访问被拒绝,需要检查访问授权等条件。ProtocolError:发生 GATT 协议错误。
这些状态的含义见 GattCommunicationStatus 文档。调用本身也可能抛出异常,实际应用应单独处理,避免把所有异常都归为设备离线。
还要注意,FromBluetoothAddressAsync() 返回的是异步操作。要检查最终的 BluetoothLEDevice 是否为 null,必须等待操作完成:
_gattDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(address);
如何排查和清理残留状态?
可以按以下顺序复测:
- 保持外设关闭,停止应用中的广播监听和设备枚举,关闭其他正在访问该设备的程序。
- 解除应用注册的事件处理器,释放持有的
GattDeviceService、GattSession和BluetoothLEDevice,再清空应用自己的设备列表及持久化记录。 - 通过 Windows 设置确认配对已移除,然后退出并重新启动应用。必要时重启 Windows 后复测,以排除进程内对象和部分运行时状态的影响。
- 分别记录配对状态、
ConnectionStatus和未缓存 GATT 操作的结果,不要只记录设备对象是否为null。
释放设备引用有助于结束应用持有的连接并释放资源,但 Dispose() 不承诺删除系统中的设备历史记录。GATT 客户端资源释放说明
已删除的 BTHPORT 注册表条目、设备管理器中的显示情况,以及重启 Bluetooth Support Service 的结果,都不足以证明系统已清除所有相关信息。没有可靠依据时,不应继续猜测并删除其他注册表位置。
如果只是希望应用“忘记设备”,可以删除应用保存的地址和选择记录,并要求用户重新扫描、选择设备。如果需要验证 Windows 完全没有该设备历史信息时的行为,更可靠的办法是在未接触过该设备的独立 Windows 测试环境中建立基线;不能把重启或手动删表当作已完成彻底清理的证据。
注意事项
未缓存的 GATT 操作可能等待较长时间,耗时会受蓝牙栈请求队列等因素影响,不宜假定固定超时时长。UWP 应用还需要在 Package.appxmanifest 中声明 bluetooth capability。GATT 客户端开发说明
备注:内容仅供参考。
