You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

更换同服务BLE设备后GetGattServicesAsync冻结问题求助

更换同型号BLE设备后Windows BLE API异常问题

问题现象

  • 单台Nonin WristOx BLE 3150设备可正常配对、连接、获取数据;取消配对后重连原设备也无异常。
  • 但取消配对后,配对另一台同型号、同服务特征的设备时,出现以下异常:
    1. 调用DeviceInformation.FindAllAsync(GattDeviceService.GetDeviceSelectorFromUuid(MyServiceId))返回空集合,即使设备已成功配对。
    2. 调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 20:26:15