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

C# 如何从运行在Windows 10 Creators Update上的WPF应用程序注册BLE通知?,c#,windows,bluetooth,bluetooth-lowenergy,creators-update,C#,Windows,Bluetooth,Bluetooth Lowenergy,Creators Update,我正在尝试编写一个使用WinRT Bluetooth LE API(Windows.Devices.Bluetooth命名空间)的C#应用程序。该应用程序是Windows经典桌面应用程序(WPF,非UWP)。在创建者更新之前运行Windows 10版本时,这些API会按预期运行。但是,在运行Creators Update时,应该向蓝牙设备发送数据的API不起作用。具体而言,以下方法返回成功状态代码,但不通过蓝牙无线电传输任何数据(使用蓝牙流量嗅探器进行验证): GattCharacterist

我正在尝试编写一个使用WinRT Bluetooth LE API(Windows.Devices.Bluetooth命名空间)的C#应用程序。该应用程序是Windows经典桌面应用程序(WPF,UWP)。在创建者更新之前运行Windows 10版本时,这些API会按预期运行。但是,在运行Creators Update时,应该向蓝牙设备发送数据的API不起作用。具体而言,以下方法返回成功状态代码,但不通过蓝牙无线电传输任何数据(使用蓝牙流量嗅探器进行验证):

  • GattCharacteristic.WriteClientCharacteristic配置描述符WithResultAsync
  • GattCharacteristic.WriteValueAsync
因此,为特征注册ValueChanged处理程序的任何尝试都不会起作用。由于注册从未发送到蓝牙LE设备,因此应用程序不会收到任何通知

我知道并非所有的UWP API都可以从非UWP应用程序中使用,但我希望有人已经在这种配置中成功开发了BLE应用程序(或者至少可以确认现在不可能)。在Creators更新之前,我能够连接并从可编程设备读取数据,并将数据写入可编程设备,而只有在最新版本的Windows 10中,上述问题才会显现出来。(注意:示例代码中使用的异步API是在Creators更新中添加的。我们的应用程序的早期版本使用了较旧的BLE API,但它们在运行Creators更新时也不起作用。)

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

已使用以下引用配置该项目:

  • System.Runtime.WindowsRuntime,位于:C:\Program Files(x86)\Reference Assembly\Microsoft\Framework.NETCore\v4.5\System.Runtime.WindowsRuntime.dll
  • Windows,位于:C:\Program Files(x86)\Windows Kits\10\UnionMetadata\Facade\Windows.WinMD
  • 基础契约,发现在:C:\程序文件(x86)\ Windows工具包\ 10 \引用\\0.0.1563.0\Windows .Fase.基础合同\3.0.0.0\Windows .Fase.基础合同. WINMD Un> WINDOWS.Buff.UnvialSalpCopRead,发现于:C:\程序文件(x86)\ Windows工具包\ 10 \\\\\0.0.1563.0\Windows .Fun.UnvialSalpChanPouth\4.0.0.0\Windows。
下面是我的应用程序中蓝牙代码的一个非常精简的版本。请注意,为了清晰起见,已经删除了许多错误处理等内容,但这应该可以让您大致了解我要做的事情:

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}");
            }
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
使用系统诊断;
使用System.Runtime.CompilerServices;
使用System.Runtime.InteropServices.WindowsRuntime;
使用Windows.Devices.Bluetooth;
使用Windows.Devices.Bluetooth.GenerictTributeProfile;
使用Windows.Devices.Enumeration;
使用Windows基金会;
使用Windows.Storage.Streams;
使用系统线程;
名称空间小程序
{
班级计划
{
私有列表设备=新列表(5);
静态void Main(字符串[]参数)
{
新程序().Execute();
}
私有void Execute()
{
Console.WriteLine(“启动设备监视程序…”);
字符串[]requestedProperties={“System.Devices.Aep.IsConnected”};
字符串查询=”;
//查询蓝牙设备
查询+=“(系统设备Aep协议:=\”{bb7bb05e-5972-42b5-94fc-76EAA708449}\”;
//查询具有控制器名称的设备
查询+=”和(System.ItemNameDisplay:=“GPLeft\”或System.ItemNameDisplay:=“GPRight\”);
var deviceWatcher=DeviceInformation.CreateWatcher(查询、requestedProperties、DeviceInformationKind.AssociationEndpoint);
deviceWatcher.Added+=deviceWatcher\u OnAdded;
deviceWatcher.Start();
Console.ReadLine();
}
专用异步无效DeviceWatcher\u OnAdded(DeviceWatcher发送方,DeviceInformation deviceInfo)
{
锁(设备)
{
if(foundDevices.Contains(deviceInfo.Name))
{
返回;
}
添加(deviceInfo.Name);
}
Console.WriteLine($“[{deviceInfo.Name}]DeviceWatcher_OnAdded…”);
等待连接到(设备信息);
}
专用异步任务连接到(设备信息设备信息)
{
尝试
{
//拿到设备
BluetoothLEDevice设备=等待BluetoothLEDevice.FromIdAsync(deviceInfo.Id);
Console.WriteLine($“[{device.Name}]找到的设备:connectionStatus={device?.connectionStatus}”);
//获得关贸总协定服务
睡眠(150);
Console.WriteLine($“[{device.Name}]getgatt服务”);

var gattServicesResult=await device.getgattservicesfouruidasync(新Guid(“COM安全性可能会阻止您的应用程序接收通知。请参阅以下线程以获取解决方案。我建议使用注册表黑客

我有一个类似的问题,在上面的注册表更改中,我的应用程序收到的通知很少,并且没有任何明显的原因停止。
在这个问题上浪费了很多时间,并等待Microsoft的修补程序。

最新更新纠正了这一问题。您可以