C++/WinRT GattCharacteristic ValueChanged 事件从不触发/触发器

C++/WinRT GattCharacteristic ValueChanged Event Never Fires / Triggers

GattCharacteristic.ValueChanged Event 似乎从未在非常简单的 C++/WinRT 控制台应用程序中启动。

对于具有配置的设备

下面的程序将简单

更新特征值不会触发ValueChanged事件。但是,相同的设备设置已成功通过其他蓝牙堆栈进行了测试。

#include "pch.h"
#include <iostream>
#include <Windows.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Devices.Bluetooth.h>
#include <winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h>
#include <winrt/Windows.Devices.Bluetooth.Advertisement.h>

using winrt::Windows::Devices::Bluetooth::BluetoothConnectionStatus;
using winrt::Windows::Devices::Bluetooth::BluetoothLEDevice;
using winrt::Windows::Devices::Bluetooth::BluetoothUuidHelper;
using winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementReceivedEventArgs;
using winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Devices::Bluetooth::GenericAttributeProfile;

using namespace winrt;


class WinBleCentral
{
public:
    WinBleCentral()
    {
        bleWatcher.Received(
            [this](BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs)
            {
                hstring testHstring{ std::wstring_view(L"TestDevice") };
                if (testHstring == eventArgs.Advertisement().LocalName())
                {
                    this->bleWatcher.Stop();
                    std::cout << "Matched\n";
                    BluetoothLEDevice::FromBluetoothAddressAsync(eventArgs.BluetoothAddress()).Completed(
                        [this](IAsyncOperation<BluetoothLEDevice> sender, AsyncStatus status)
                        {
                            if (auto device = sender.GetResults(); device)
                            {
                                std::cout << "Connected\n";
                                winrt::guid serviceGUID = BluetoothUuidHelper::FromShortId(0xFFFF);

                                device.GetGattServicesForUuidAsync(serviceGUID).Completed(
                                    [this](IAsyncOperation<GattDeviceServicesResult> sender, AsyncStatus status)
                                    {
                                        GattDeviceServicesResult result = sender.get();
                                        if (result && status == winrt::Windows::Foundation::AsyncStatus::Completed)
                                        {
                                            winrt::guid charGUID = BluetoothUuidHelper::FromShortId(0xEEEE);
                                            std::cout << "Num Services: " << result.Services().Size() << '\n';
                                            for (auto&& service : result.Services())
                                            {
                                                service.GetCharacteristicsForUuidAsync(charGUID).Completed(
                                                    [this](IAsyncOperation<GattCharacteristicsResult>sender, AsyncStatus status)
                                                    {
                                                        std::cout << "Get Characteristics\n";
                                                        if (auto result = sender.GetResults(); result)
                                                        {
                                                            std::cout << "Num Characteristics: " << result.Characteristics().Size() << '\n';
                                                            for (auto character : result.Characteristics())
                                                            {
                                                                character.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue::Notify).Completed(
                                                                    [this, character](IAsyncOperation<GattCommunicationStatus>sender, AsyncStatus status)
                                                                    {
                                                                        character.ValueChanged([this](GattCharacteristic characteristic, GattValueChangedEventArgs const& args)
                                                                            {
                                                                                std::cout << "New Value!\n";
                                                                            });
                                                                    });
                                                            }
                                                        }
                                                    });
                                            }
                                        }
                                    }
                                );

                            }
                        });
                }
            });
        bleWatcher.Start();
    };

    BluetoothLEAdvertisementWatcher bleWatcher;
};

int main()
{
    WinBleCentral bleCentral;
    while (getchar() != '\n');
}

这似乎是由于 GattCharacteristic 对象超出了范围。通过简单地保留对 tha 变量的引用,上述工作按预期进行。

下面是与上面相同的程序,但为了提高可读性,去掉了 Async 的一面。我已经测试过,两种情况下显示的行为完全相同。

#include "pch.h"
#include <iostream>

#include <Windows.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Devices.Bluetooth.h>
#include <winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h>
#include <winrt/Windows.Devices.Bluetooth.Advertisement.h>

using winrt::Windows::Devices::Bluetooth::BluetoothConnectionStatus;
using winrt::Windows::Devices::Bluetooth::BluetoothLEDevice;
using winrt::Windows::Devices::Bluetooth::BluetoothUuidHelper;
using winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementReceivedEventArgs;
using winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::Devices::Bluetooth::GenericAttributeProfile;

using namespace winrt;

class WinBleCentral
{
public:
 
    WinBleCentral()
    {
        std::cout << "Start\n";
        bleWatcher.Received(
            [this](BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs)
            {
                hstring testHstring{ std::wstring_view(L"TestDevice") };
                if (testHstring == eventArgs.Advertisement().LocalName())
                {
                    this->bleWatcher.Stop();
                    std::cout << "Matched\n";
                    auto device = BluetoothLEDevice::FromBluetoothAddressAsync(eventArgs.BluetoothAddress()).get();
                    std::cout << "Device\n";
                    winrt::guid serviceGUID = BluetoothUuidHelper::FromShortId(0xFFFF);
                    auto serviceResults = device.GetGattServicesForUuidAsync(serviceGUID).get();
                    std::cout << "Service Results\n";
                    winrt::guid charGUID = BluetoothUuidHelper::FromShortId(0xEEEE);
                    auto service = serviceResults.Services().GetAt(0);
                    std::cout << "Services\n";
                    auto characteristicResults = service.GetCharacteristicsForUuidAsync(charGUID).get();
                    std::cout << "Characteristic Results\n";
                    auto characteristic = characteristicResults.Characteristics().GetAt(0);
                    characteristics.push_back(characteristic);
                    std::cout << "Characterstic\n";
                    auto gattCommunicationStatus = characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue::Notify).get();
                    std::cout << "WriteClientCharacteristicConfiguration\n";
                    characteristic.ValueChanged([this](GattCharacteristic const& notifyingCharacteristic, GattValueChangedEventArgs args)
                        {
                            std::cout << "New Value!\n";
                        });
                }
            });
        bleWatcher.Start();
    }

    std::vector<GattCharacteristic> characteristics;
    BluetoothLEAdvertisementWatcher bleWatcher;
};


int main()
{
    WinBleCentral bleCentral;
    while (getchar() != '\n');
}

即使从未使用过 std::vector<GattCharacteristic> characteristics,但 characteristics.push_back(characteristic) 的简单操作足以使引用保持活动状态。这种行为绝对出乎意料。

为了与整个 UWP 风格保持一致,我最初使用了 IVector and the Append 方法,但整个事情在 check_hresult(WINRT_IMPL_SHIM(winrt::Windows::Foundation::Collections::IVector<T>)->Append(impl::bind_in(value))); 崩溃了,因为它似乎是一个空指针。

对于上述内容,为了便于阅读,所有错误检查都被避免了。实际实施应该对所有异步操作进行健康检查,以确保数据确实存在。