FromBluetoothAddressAsync 在 mscorlib.ni.dll 中抛出 'System.IO.FileNotFoundException'

FromBluetoothAddressAsync throws 'System.IO.FileNotFoundException' in mscorlib.ni.dll

我正在开发一个连接到 BLE 信标的应用程序,为此我使用了 BluetoothLEAdvertisementWatcher API。当我收到广告时,我想连接到设备以读取 GATT 特性。

所以我启动了一个 BLEwatcher

    BluetoothLEAdvertisementWatcher watcher;
    watcher.Received += OnAdvertisementReceived;
    watcher.Stopped += OnAdvertisementWatcherStopped;
    watcher.Start();

然后我尝试访问设备

private async void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs)
{
    var address = eventArgs.BluetoothAddress;

    BluetoothLEDevice device = await BluetoothLEDevice.FromBluetoothAddressAsync(eventArgs.BluetoothAddress);
    Debug.WriteLine(device.Name + " - " + device.DeviceId);
    ....

这失败了(在 FromBluetoothAddressAsync 行)

An exception of type 'System.IO.FileNotFoundException' occurred in mscorlib.ni.dll but was not handled in user code

附加信息:系统找不到指定的文件。 (HRESULT 异常:0x80070002)

有趣的是:如果我打开系统的蓝牙设备 window 它工作正常!

因此,当我打开蓝牙设备 window 和 运行 应用程序时,不会抛出错误,当我关闭蓝牙设备 window 时,它会抛出错误。

请注意,它总是在后台任务中抛出此错误。

显然它确实适用于版本 10.0.10586.218。 我在网上从有同样问题的人那里找到了这个:

LUMIA 950, Windows 10, 1511, 10.0.14332.1001

Exception thrown on FromIdAsync(): 'System.IO.FileNotFoundException' in mscorlib.ni.dll

LUMIA 730, Windows 10, 1511, 10.0.10586.218

Exception thrown on FindAllAsync(): 'System.ArgumentException'

LUMIA 920, Windows 10, 1511, 10.0.10586.218

No Error!

截图:

BLE Windows API 和它们为您提供的很好的(未)记录的异常不是很可爱吗?

我猜,如果您打算与设备配对,则不应使用 AdvertisementWatcher。而是将 DeviceInformation 观察器与包含配对和未配对设备的 BLE 设备的特定选择器一起使用。这将提供一个随时可用的 DeviceInformation 对象,您可以与之配对。

示例代码可以在 https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/DeviceEnumerationAndPairing,场景编号 8 中看到。

问题

此错误是由于在非UI线程中调用FromBluetoothAddressAsync引起的。 Windows 10 在应用程序尝试访问蓝牙外围设备时需要用户同意。调用 FromBluetoothAddressAsync 时,Windows 会尝试创建同意对话。因为这不是 UI 线程,同意对话框无法出现并抛出异常。

解决方案

首先,您应该使 BluetoothLEAdvertisementWatcher 更具体地针对您要搜索的外围设备。如果你不这样做,那么你的应用程序将看到每一个蓝牙 LE 设备并提示用户同意每个设备 - 糟糕的用户体验。

有多种过滤广告的方法。您可以使用 eventArgs 中的属性在 OnAdvertisementReceived 方法中手动执行此操作,或者您可以在 BluetoothLEAdvertisementWatcher 本身中设置过滤器。例如,要查找具有心率监测器的蓝牙 LE 设备,请执行:

watcher.AdvertisementFilter.Advertisement.ServiceUuids.Add(GattServiceUuids.HeartRate);

要在 UI 线程上进行调用,您需要使用调度程序。您可以使用以下代码设置调度程序:

// Variable in your class
CoreDispatcher dispatcher;

// Init for your class
public MainPage() {
    ...
    // Init the dispatcher, this must be done on the main thread
    this.dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
    ...
}

然后,当您收到广告时,您可以进行自己的过滤,然后告诉调度程序在主线程上连接到蓝牙 LE 设备。

private async void ConnectToBTDevice(BluetoothLEAdvertisementReceivedEventArgs eventArgs) {
    // This will cause a consent prompt if the user hasn't already consented
    var btDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(eventArgs.BluetoothAddress);
}

private bool Filter(BluetoothLEAdvertisementReceivedEventArgs eventArgs) {
    // Return true if the advertisement has the name we're looking for
    return eventArgs.Advertisement.LocalName != null && eventArgs.Advertisement.LocalName.Equals("Peripheral Name");
}

private void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs) {

    // Do our own filtering (for example)
    if(!this.Filter(eventArgs)) return;

    // Connect to the Bluetooth device in the UI (main) thread
    this.dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => this.ConnectToBTDevice(eventArgs));
}