how to use BluetoothGATTGetCharacteristic with C#

0

I code a C# Console application ,try get my headphone battery info

class BluetoothAPIs

class BluetoothAPIs
{
    //https://docs.microsoft.com/zh-cn/windows/win32/api/bluetoothleapis/nf-bluetoothleapis-bluetoothgattgetservices
    [DllImport("BluetoothAPIs.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int BluetoothGATTGetServices(
        IntPtr hDevice,
        UInt16 ServicesBufferCount,
        IntPtr ServicesBuffer,
        ref UInt16 ServicesBufferActual,
        UInt32 Flags
    );

    //https://docs.microsoft.com/zh-cn/windows/win32/api/bthledef/ns-bthledef-bth_le_uuid
    [StructLayout(LayoutKind.Sequential)]
    public struct BTH_LE_UUID
    {
        public Boolean isShortUuid;
        public UInt16 ShortUuid;
        public Guid LongUuid;
    }

    //https://docs.microsoft.com/zh-cn/windows/win32/api/bthledef/ns-bthledef-bth_le_gatt_service
    [StructLayout(LayoutKind.Sequential)]
    public struct BTH_LE_GATT_SERVICE
    {
        public BTH_LE_UUID ServiceUuid;
        public UInt16 AttributeHandle;
    }

    //https://docs.microsoft.com/en-us/windows/win32/api/bluetoothleapis/nf-bluetoothleapis-bluetoothgattgetcharacteristics
    [DllImport("BluetoothAPIs.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int BluetoothGATTGetCharacteristics(
        IntPtr hDevice,
        ref BTH_LE_GATT_SERVICE Service,
        UInt16 CharacteristicsBufferCount,
        IntPtr CharacteristicsBuffer,
        ref UInt16 CharacteristicsBufferActual,
        UInt32 Flags
    );

    //https://docs.microsoft.com/en-us/windows/win32/api/bthledef/ns-bthledef-bth_le_gatt_characteristic
    [StructLayout(LayoutKind.Sequential)]
    public struct BTH_LE_GATT_CHARACTERISTIC
    {
        public UInt16 ServiceHandle;
        public BTH_LE_UUID CharacteristicUuid;
        public UInt16 AttributeHandle;
        public UInt16 CharacteristicValueHandle;
        public Boolean IsBroadcastable;
        public Boolean IsReadable;
        public Boolean IsWritable;
        public Boolean IsWritableWithoutResponse;
        public Boolean IsSignedWritable;
        public Boolean IsNotifiable;
        public Boolean IsIndicatable;
        public Boolean HasExtendedProperties;
    }

    //https://docs.microsoft.com/en-us/windows/win32/api/bluetoothleapis/nf-bluetoothleapis-bluetoothgattgetcharacteristicvalue
    [DllImport("BluetoothAPIs.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int BluetoothGATTGetCharacteristicValue(
        IntPtr hDevice,
        ref BTH_LE_GATT_CHARACTERISTIC Characteristic,
        UInt32 CharacteristicValueDataSize,
        ref BTH_LE_GATT_CHARACTERISTIC_VALUE CharacteristicValue,
        out UInt16 CharacteristicValueSizeRequired,
        UInt32 Flags
    );

    //https://docs.microsoft.com/en-us/windows/win32/api/bthledef/ns-bthledef-bth_le_gatt_characteristic_value
    [StructLayout(LayoutKind.Sequential)]
    public struct BTH_LE_GATT_CHARACTERISTIC_VALUE
    {
        public UInt32 DataSize;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] public byte[] Data;
    }

    public static readonly UInt32 BLUETOOTH_GATT_FLAG_NONE = 0;
    public static readonly UInt32 BLUETOOTH_GATT_FLAG_FORCE_READ_FROM_DEVICE = 0x00000004;
}

https://docs.microsoft.com/en-us/windows/win32/api/bluetoothleapis/nf-bluetoothleapis-bluetoothgattgetcharacteristics

I try this:

IntPtr characteristicsBufferPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(BluetoothAPIs.BTH_LE_GATT_CHARACTERISTIC)) * characteristicsBufferCount);
            UInt16 characteristicsBufferActual = 0;
            BluetoothAPIs.BluetoothGATTGetCharacteristics(hDevice, ref service, characteristicsBufferCount, characteristicsBufferPtr, ref characteristicsBufferActual, BluetoothAPIs.BLUETOOTH_GATT_FLAG_NONE);
            for (int n = 0; n < characteristicsBufferActual; n++)
            {
                BluetoothAPIs.BTH_LE_GATT_CHARACTERISTIC characteristic = Marshal.PtrToStructure<BluetoothAPIs.BTH_LE_GATT_CHARACTERISTIC>(characteristicsBufferPtr + n * Marshal.SizeOf(typeof(BluetoothAPIs.BTH_LE_GATT_CHARACTERISTIC)));
                BluetoothAPIs.BTH_LE_GATT_CHARACTERISTIC_VALUE characteristicValue = new BluetoothAPIs.BTH_LE_GATT_CHARACTERISTIC_VALUE();
                UInt16 characteristicValueSizeRequired = 0;
                BluetoothAPIs.BluetoothGATTGetCharacteristicValue(hDevice, ref characteristic, 256, ref characteristicValue, out characteristicValueSizeRequired, BluetoothAPIs.BLUETOOTH_GATT_FLAG_NONE);
                if (characteristic.CharacteristicUuid.ShortUuid == 0x2A19) // battery level
                {
                    Console.WriteLine("Battery Level: {0}", characteristicValue.Data[0]);
                }
                else if (characteristic.CharacteristicUuid.ShortUuid == 0x2A01) // appearance
                {
                    int appearance = BitConverter.ToInt32(characteristicValue.Data);
                    string appearanceName = appearanceDict.GetValueOrDefault(appearance, "Unknown");
                    Console.WriteLine("Appearance: {1}, Data: {2}", appearance, appearanceName);
                }
                else //string
                {
                    Console.WriteLine("UUID: {0}, DataSize: {1}, Data: {2}", characteristic.CharacteristicUuid.ShortUuid, characteristicValue.DataSize, Encoding.ASCII.GetString(characteristicValue.Data, 0, (int)characteristicValue.DataSize));
                }
            }

When n = 0, I can get a struct 'BTH_LE_GATT_CHARACTERISTIC'. But n > 0, get struct fail from ptr. How to get next '' struct or get struct array

There are full code: https://gist.github.com/cd2b1a504ee9155e8dcb2cc7c62257d7.git

c#
winapi
bluetooth
bluetooth-lowenergy
asked on Stack Overflow Oct 22, 2019 by MUEDSA

1 Answer

0

The result of Marshal.SizeOf(typeof(BluetoothAPIs.BTH_LE_GATT_CHARACTERISTIC)) is 60. However, in C++, sizeof(BTH_LE_GATT_CHARACTERISTIC) is 36.

The issues is:

Boolean in C# is 4 bytes while BOOLEAN in C++ is one byte.

So in C#, the corresponding definitions of BTH_LE_UUID and BTH_LE_GATT_CHARACTERISTIC will like this:

    [StructLayout(LayoutKind.Sequential)]
    public struct BTH_LE_UUID
    {
        public Byte isShortUuid;
        public UInt16 ShortUuid;
        public Guid LongUuid;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct BTH_LE_GATT_CHARACTERISTIC
    {
        public UInt16 ServiceHandle;
        public BTH_LE_UUID CharacteristicUuid;
        public UInt16 AttributeHandle;
        public UInt16 CharacteristicValueHandle;
        public Byte IsBroadcastable;
        public Byte IsReadable;
        public Byte IsWritable;
        public Byte IsWritableWithoutResponse;
        public Byte IsSignedWritable;
        public Byte IsNotifiable;
        public Byte IsIndicatable;
        public Byte HasExtendedProperties;
    }
answered on Stack Overflow Oct 23, 2019 by Rita Han

User contributions licensed under CC BY-SA 3.0