如何在 C# 中使用 BluetoothGATTGetCharacteristic

how to use BluetoothGATTGetCharacteristic with C#

我编写了一个 C# 控制台应用程序,尝试获取我的耳机电池信息

class BluetoothAPI

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

我试试这个:

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));
                }
            }

当n = 0时,我可以得到一个结构体'BTH_LE_GATT_CHARACTERISTIC'。 但是 n > 0,从 ptr 获取结构失败。 如何获取下一个''结构或获取结构数组

有完整代码:https://gist.github.com/cd2b1a504ee9155e8dcb2cc7c62257d7.git

Marshal.SizeOf(typeof(BluetoothAPIs.BTH_LE_GATT_CHARACTERISTIC))的结果是60。但是,在 C++ 中,sizeof(BTH_LE_GATT_CHARACTERISTIC)36.

问题是:

Boolean 在 C# 中是 4 个字节,而在 C++ 中 BOOLEAN 是一个字节。

所以在C#中,BTH_LE_UUIDBTH_LE_GATT_CHARACTERISTIC对应的定义会是这样:

    [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;
    }