2017-08-29 81 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)中可用的最新版本中得到解決。 –

相關問題