繁体   English   中英

如何从Windows 10 Creators Update上运行的WPF应用程序注册BLE通知?

[英]How to register for BLE notifcations from a WPF app running on Windows 10 Creators Update?

我试图编写一个使用WinRT蓝牙LE API(Windows.Devices.Bluetooth命名空间)的C#应用​​程序。 该应用程序是Windows Classic Desktop应用程序(WPF, 而不是 UWP)。 当运行Creators Update之前的Windows 10版本时,这些API会按预期运行。 但是,在运行Creators Update时,应将数据发送到Bluetooth设备的API无法正常工作。 具体来说,以下方法返回成功状态代码,但不通过蓝牙无线电传输任何数据(使用蓝牙流量嗅探器进行验证):

  • GattCharacteristic.WriteClientCharacteristicConfigurationDescriptorWithResultAsync
  • GattCharacteristic.WriteValueAsync

结果,任何为特性注册ValueChanged处理程序的尝试均不起作用。 由于注册永远不会发送到Bluetooth LE设备,因此应用程序不会收到任何通知。

我知道不是所有的UWP API都可以从非UWP应用程序中使用,但是我希望有人能够以这种配置成功开发BLE应用程序(或者至少可以确认现在不可能)。 在Creators Update之前,我能够从BLE设备进行连接和读取数据,以及将数据写入BLE设备,并且仅在Windows 10的最新版本中,上述问题才会显现。 (注意:示例代码中使用的Async API已在“创建者更新”中添加。我们应用程序的先前版本使用了较早的BLE API,但在运行“创建者更新”时也无法正常工作。)

具体来说,我的问题是: 给定以下项目参考列表和示例代码,我是否可以尝试从非UWP应用程序运行运行Creators Update的Windows 10上获得有效的Bluetooth LE连接? 请注意,“将应用程序转换为UWP应用程序”这一明显答案对我们不起作用,因为我们以其他方式与其他硬件和文件进行交互,而这种方式在UWP沙箱中是不可能的。

该项目配置有以下参考:

  • System.Runtime.WindowsRuntime,位于:C:\\ Program Files(x86)\\ Reference Assemblies \\ Microsoft \\ Framework.NETCore \\ v4.5 \\ System.Runtime.WindowsRuntime.dll
  • Windows,位于:C:\\ Program Files(x86)\\ Windows Kits \\ 10 \\ UnionMetadata \\ Facade \\ Windows.WinMD
  • Windows.Foundation.FoundationContract,位于:C:\\ Program Files(x86)\\ Windows Kits \\ 10 \\ References \\ 10.0.15063.0 \\ Windows.Foundation.FoundationContract \\ 3.0.0.0 \\ Windows.Foundation.FoundationContract.winmd
  • Windows.Foundation.UniversalApiContract,位于:C:\\ Program Files(x86)\\ Windows Kits \\ 10 \\ References \\ 10.0.15063.0 \\ Windows.Foundation.UniversalApiContract \\ 4.0.0.0 \\ Windows.Foundation.UniversalApiContract.winmd

以下是我的应用程序中非常简化的蓝牙代码版本。 请注意,为清楚起见,已删除了许多错误处理等内容,但是它应该可以大致了解我要执行的操作:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
using Windows.Foundation;
using Windows.Storage.Streams;
using System.Threading;

namespace BLEMinimumApp
{
    class Program
    {
        private List<string> foundDevices = new List<string>(5);

        static void Main(string[] args)
        {
            new Program().Execute();
        }

        private void Execute()
        {
            Console.WriteLine("Starting device watcher...");

            string[] requestedProperties = { "System.Devices.Aep.IsConnected" };
            String query = "";
            //query for Bluetooth LE devices
            query += "(System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\")";
            //query for devices with controllers' name
            query += " AND (System.ItemNameDisplay:=\"GPLeft\" OR System.ItemNameDisplay:=\"GPRight\")";

            var deviceWatcher = DeviceInformation.CreateWatcher(query, requestedProperties, DeviceInformationKind.AssociationEndpoint);
            deviceWatcher.Added += DeviceWatcher_OnAdded;
            deviceWatcher.Start();

            Console.ReadLine();
        }

        private async void DeviceWatcher_OnAdded(DeviceWatcher sender, DeviceInformation deviceInfo)
        {
            lock (foundDevices)
            {
                if (foundDevices.Contains(deviceInfo.Name))
                {
                    return;
                }
                foundDevices.Add(deviceInfo.Name);
            }

            Console.WriteLine($"[{deviceInfo.Name}] DeviceWatcher_OnAdded...");
            await ConnectTo(deviceInfo);
        }

        private async Task ConnectTo(DeviceInformation deviceInfo)
        {
            try
            {
                // get the device
                BluetoothLEDevice device = await BluetoothLEDevice.FromIdAsync(deviceInfo.Id);
                Console.WriteLine($"[{device.Name}] Device found: connectionStatus={device?.ConnectionStatus}");

                // get the GATT service
                Thread.Sleep(150);
                Console.WriteLine($"[{device.Name}] Get GATT Services");
                var gattServicesResult = await device.GetGattServicesForUuidAsync(new Guid("<GUID REMOVED FOR SO POST"));
                Console.WriteLine($"[{device.Name}] GATT services result: status={gattServicesResult?.Status}, count={gattServicesResult?.Services?.Count}, cx={device.ConnectionStatus}");

                if (gattServicesResult == null 
                    || gattServicesResult.Status != GattCommunicationStatus.Success 
                    || gattServicesResult.Services == null 
                    || gattServicesResult.Services?.Count < 1)
                {
                    Console.WriteLine($"[{device.Name}] Failed to find GATT service.");
                    return;
                }

                var service = gattServicesResult.Services[0];
                Console.WriteLine($"[{device?.Name}] GATT service found: gattDeviceService={service.Uuid}");

                // get the GATT characteristic
                Thread.Sleep(150);
                Console.WriteLine($"[{device.Name}] Get GATT characteristics");
                var gattCharacteristicsResult = await service.GetCharacteristicsForUuidAsync(new Guid("<GUID REMOVED FOR SO POST>"));
                Console.WriteLine($"[{device.Name}] GATT Characteristics result: status={gattCharacteristicsResult?.Status}, count={gattCharacteristicsResult?.Characteristics?.Count}, cx={device.ConnectionStatus}");

                if (gattCharacteristicsResult == null
                    || gattCharacteristicsResult.Status != GattCommunicationStatus.Success
                    || gattCharacteristicsResult.Characteristics == null
                    || gattCharacteristicsResult.Characteristics?.Count < 1)
                {
                    Console.WriteLine($"[{device.Name}] Failed to find GATT characteristic.");
                    return;
                }

                var characteristic = gattCharacteristicsResult.Characteristics[0];

                // register for notifications
                Thread.Sleep(150);

                characteristic.ValueChanged += (sender, args) =>
                {
                    Console.WriteLine($"[{device.Name}] Received notification containing {args.CharacteristicValue.Length} bytes");
                };
                Console.WriteLine($"[{device.Name}] Writing CCCD...");
                GattWriteResult result =
                    await characteristic.WriteClientCharacteristicConfigurationDescriptorWithResultAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
                Console.WriteLine($"[{device?.Name}] Characteristics write result: status={result.Status}, protocolError={result.ProtocolError}");

                // send configuration to device 
                await SendConfiguration(device, characteristic);
            }
            catch (Exception ex) when((uint) ex.HResult == 0x800710df)
            {
                Console.WriteLine("bluetooth error 1");
                // ERROR_DEVICE_NOT_AVAILABLE because the Bluetooth radio is not on.
            }
        }

        private async Task SendConfiguration(BluetoothLEDevice device, GattCharacteristic characteristic)
        {
            if (characteristic != null)
            {
                var writer = new DataWriter();
                // CONFIGURATION REMOVED, but this code writes device-specific bytes to the DataWriter

                await SendMessage(device, characteristic, writer.DetachBuffer());
            }
        }

        private async Task SendMessage(BluetoothLEDevice device, GattCharacteristic characteristic, IBuffer message)
        {
            if (characteristic != null && device.ConnectionStatus.Equals(BluetoothConnectionStatus.Connected) && message != null)
            {
                Console.WriteLine($"[{device.Name}] Sending message...");
                GattCommunicationStatus result = await characteristic.WriteValueAsync(message);
                Console.WriteLine($"[{device.Name}] Result: {result}");
            }
        }
    }
}

COM安全性可能阻止您的应用程序接收通知。 请参考以下主题以获取解决方案。 我建议使用注册表黑客。

https://social.msdn.microsoft.com/Forums/en-US/58da3fdb-a0e1-4161-8af3-778b6839f4e1/bluetooth-bluetoothledevicefromidasync-does-not-complete-on-10015063?forum=wdk

我遇到了类似的问题,并且在更改了上述注册表后,我的应用收到的通知很少,并且没有任何明显的原因停止。 在这个问题上浪费了很多时间,等待微软的补丁。

最新更新纠正了此问题。 您也可以使用其他海报引用的链接上的Matt Beaver的说明来解决此问题。

基本上是:

  1. 为您的应用程序指定一个AppId
  2. 在您的应用程序中致电CoInitializeSecurity以允许我们回叫

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM