2017-08-29 98 views
1

我正在尝试编写一个使用WinRT Bluetooth LE API(Windows.Devices.Bluetooth命名空间)的C#应用​​程序。该应用程序是Windows经典桌面应用程序(WPF,而不是 UWP)。在创作者更新之前运行Windows 10版本时,这些API按预期运行。但是,运行创作者更新时,应将数据发送到蓝牙设备的API不起作用。具体来说,有以下方法返回成功状态码,但不要通过蓝牙无线电的任何数据(比如使用蓝牙流量嗅探器验证):如何从Windows 10创建者更新上运行的WPF应用程序注册BLE通知?

  • GattCharacteristic.WriteClientCharacteristicConfigurationDescriptorWithResultAsync
  • GattCharacteristic.WriteValueAsync

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

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

具体来说,我的问题是:给出了以下项目参考列表和示例代码,是否有任何我可以尝试在运行非UWP应用程序的创作者更新的Windows 10上使用Bluetooth LE连接?请注意,“将应用程序转换为UWP应用程序”的显而易见的答案对我们来说不起作用,因为我们以UWP沙箱内无法实现的方式与其他硬件和文件进行交互。

该项目具有以下引用配置:

  • System.Runtime.WindowsRuntime,在发现:C:\ Program Files文件(x86)的\参考大会\微软\ Framework.NETCore \ V4.5 \ System.Runtime.WindowsRuntime.dll
  • Windows中,发现:C:\ Program Files文件(x86)的\的Windows套件\ 10 \ UnionMetadata \外观\ 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}"); 
      } 
     } 
    } 
} 

回答

1

COM安全可能阻止你的应用程序从接收通知。请参考下面的线索来获得解决方案。我建议使用注册表黑客。

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

我有一个类似的问题,并与上述注册表更改我的应用程序接收一些通知,并没有任何明显的理由停止。 浪费了很多时间在这个问题上,并等待来自微软的补丁。

+0

感谢您的答复。我指定了该线程中描述的AppID,这似乎解决了该问题。还没有完成详尽的测试,看看通知是否停止,如你所描述的。希望不是。 :) –

1

最新的更新修正了这一点。您也可以使用Matt Beaver的指示来解决该问题,其他海报引用的链接。

基本上之一:

  1. 为应用程序指定的AppId
  2. 呼叫CoInitializeSecurity在您的应用程序,让我们再打
+0

感谢您的回应。按照vt08505链接的线程中描述的指定AppID似乎可以解决我们的问题。我还证实,这似乎在Windows内部人员程序(操作系统版本16278.1000)中可用的最新版本中得到解决。 –

相关问题