更换同服务BLE设备后GetGattServicesAsync冻结问题求助
更换同型号BLE设备后Windows BLE API异常问题
问题现象
- 单台Nonin WristOx BLE 3150设备可正常配对、连接、获取数据;取消配对后重连原设备也无异常。
- 但取消配对后,配对另一台同型号、同服务特征的设备时,出现以下异常:
- 调用
DeviceInformation.FindAllAsync(GattDeviceService.GetDeviceSelectorFromUuid(MyServiceId))返回空集合,即使设备已成功配对。 - 调用
BluetoothLEDevice.GetGattServicesAsync()会无限挂起,永不返回结果。
- 调用
- 唯一临时修复方式:在任务管理器终止
bthserv(Bluetooth Support Service)进程,待其自动重启后恢复正常。
相关代码
设备查找逻辑
int retryCount = 5; DeviceInformation deviceInfo = null; // 有时Windows会丢失服务记录!可能需要刷新,有时需多次重试。 while (deviceInfo == null && retryCount-- > 0) { // 切换设备后,即使已成功配对第二台同服务设备,此调用通常返回空集合 var allDeviceInfos = await DeviceInformation.FindAllAsync(GattDeviceService.GetDeviceSelectorFromUuid(MyServiceId)); deviceInfo = allDeviceInfos.FirstOrDefault(d => d.Name == device.DeviceInformation.Name); if (deviceInfo == null) { _logger.LogDebug("Device not found! Retrying..."); await RefreshServices(); Thread.Sleep(1000); } }
服务刷新逻辑
private async Task RefreshServices() { _logger.LogDebug("Refreshing the device's service list."); var devices = await DeviceInformation.FindAllAsync(BluetoothLEDevice.GetDeviceSelector()); var myDevices = new List<DeviceInformation>(); foreach (var device in devices.Where(d => d.Name.StartsWith("MyDeviceName_"))) { var bleDevice = await BluetoothLEDevice.FromIdAsync(device.Id); // 切换设备后,应用会在此行挂起,永不返回值 var services = await bleDevice.GetGattServicesAsync(); _logger.LogDebug($"Service list from {wristOx.Name}: Status: {services.Status}, count: {services.Services?.Count}"); } }
补充细节
- 尝试使用
GetGattServicesAsync(BluetoothMode.Uncached),问题依旧; - 重启电脑无法解决
GetGattServicesAsync()挂起问题,但终止bthserv可修复; DeviceInformation.FindAllAsync(BluetoothLEDevice.GetDeviceSelector())能正常返回目标设备,但按服务UUID筛选的FindAllAsync返回空;- 系统差异:Windows 11 23H2、Windows 10 22H2的Surface Go 2出现问题,Windows 10 21H2的Surface Go 2无此问题;
- 关键线索:设备实例化后
GattServices属性显示仅含通用服务0x1801时,调用GetGattServicesAsync会挂起;终止bthserv后GattServices为空,此时调用可正常返回所有服务。
可行解决方案与优化建议
1. 程序化重启Bluetooth Support Service
既然手动终止bthserv有效,可以通过代码自动重启该服务,替代手动操作:
using System.ServiceProcess; private void RestartBluetoothSupportService() { var service = new ServiceController("bthserv"); if (service.Status == ServiceControllerStatus.Running) { service.Stop(); service.WaitForStatus(ServiceControllerStatus.Stopped); } service.Start(); service.WaitForStatus(ServiceControllerStatus.Running); }
注意:此操作需要管理员权限,需确保应用以管理员身份运行,或在代码中请求权限。
2. 优化设备发现与服务刷新逻辑
替代RefreshServices的轻量方案
原RefreshServices遍历所有设备并调用GetGattServicesAsync可能触发挂起,可改为直接清理设备缓存并重新枚举:
private async Task RefreshDeviceCache() { // 强制刷新BLE设备缓存 await DeviceInformation.UpdateAsync(); // 重新枚举所有BLE设备并清理旧实例 var bleDevices = await DeviceInformation.FindAllAsync(BluetoothLEDevice.GetDeviceSelector()); foreach (var dev in bleDevices.Where(d => d.Name.StartsWith("MyDeviceName_"))) { if (await BluetoothLEDevice.FromIdAsync(dev.Id) is { } bleDevice) { bleDevice.Dispose(); } } }
调整服务查找策略
避免依赖按服务UUID筛选FindAllAsync,改为先通过设备名称/地址找到DeviceInformation,再直接获取设备实例并查询服务:
var targetDevice = (await DeviceInformation.FindAllAsync(BluetoothLEDevice.GetDeviceSelector())) .FirstOrDefault(d => d.Name == targetDeviceName); if (targetDevice != null) { var bleDevice = await BluetoothLEDevice.FromIdAsync(targetDevice.Id); if (bleDevice != null) { // 先检查GattServices是否残留旧缓存,若有则重新实例化设备 if (bleDevice.GattServices.Count > 0) { bleDevice.Dispose(); bleDevice = await BluetoothLEDevice.FromIdAsync(targetDevice.Id); } // 强制使用非缓存模式获取服务 var servicesResult = await bleDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); if (servicesResult.Status == GattCommunicationStatus.Success) { var targetService = servicesResult.Services.FirstOrDefault(s => s.Uuid == MyServiceId); // 后续业务操作... } } }
3. 系统层面的临时规避
- 对于Windows 10 22H2/Windows 11 23H2,尝试安装最新的蓝牙驱动更新(尤其是Surface设备的官方驱动);
- 若设备支持,尝试使用蓝牙地址而非设备名称进行设备匹配,避免同型号设备名称冲突导致的缓存混淆。
内容的提问来源于stack exchange,提问作者komodosp
相关产品推荐
相关产品推荐

