使用 win32 API 从 c# 注册 GATT 特征值更改通知

问题描述 投票:0回答:1

我正在尝试使用 c# 中的 win32 API 注册 GATT 特征值更改通知。我能够获取设备的句柄并获取相关的 GATT 服务和特性。当我尝试使用

BluetoothGATTRegisterEvent
注册值更改通知时,问题就出现了: https://learn.microsoft.com/en-us/windows/win32/api/bluetoothleapis/nf-bluetoothleapis-bluetoothgattregisterevent

调用该方法时,我总是得到 0x57 (E_INVALIDARG)。不幸的是,我没有得到任何关于什么 arg 无效的提示...我尝试了许多不同的变体,但没有运气。以下是用于调用BluetoothGATTRegisterEvent 的结构体。我省略了服务和特征发现,因为这似乎工作正常。

任何对我的定义和调用

BluetoothGATTRegisterEvent
可能有问题的建议将不胜感激。

public enum BTH_LE_GATT_EVENT_TYPE
{
    CharacteristicValueChangedEvent = 0
}

[StructLayout(LayoutKind.Sequential)]
public struct BLUETOOTH_GATT_NOTIFICATION_REGISTRATION
{
    public ushort NumCharacteristics;
    public BTH_LE_GATT_CHARACTERISTIC[] Characteristics;
}

[StructLayout(LayoutKind.Sequential)]
public struct BLUETOOTH_GATT_EVENT_HANDLE
{
    public IntPtr handle;
}

// Define the callback delegate
internal delegate void PFNBLUETOOTH_GATT_EVENT_CALLBACK(
    BTH_LE_GATT_EVENT_TYPE EventType,
    IntPtr EventOutParameter,
    IntPtr Context
);

[DllImport("BluetoothApis.dll", SetLastError = true)]
public static extern uint BluetoothGATTRegisterEvent(
    ushort Service,
    BTH_LE_GATT_EVENT_TYPE eventType,
    ref BLUETOOTH_GATT_NOTIFICATION_REGISTRATION eventParameterIn,
    PFNBLUETOOTH_GATT_EVENT_CALLBACK callback,
    IntPtr context,
    ref BLUETOOTH_GATT_EVENT_HANDLE eventHandle,
    uint Flags
);

public void RegisterEvents(
    SafeFileHandle deviceHandle,
    BTH_LE_GATT_SERVICE service,
    BTH_LE_GATT_CHARACTERISTIC characteristic,
    PFNBLUETOOTH_GATT_EVENT_CALLBACK eventHandler
)
{
    var eventHandle = new BLUETOOTH_GATT_EVENT_HANDLE();
    var eventParameters = new BLUETOOTH_GATT_NOTIFICATION_REGISTRATION
    {
        NumCharacteristics = 1,
        Characteristics = new[] { characteristic }
    };

    BluetoothGATTRegisterEvent(
        characteristic.ServiceHandle,
        BTH_LE_GATT_EVENT_TYPE.CharacteristicValueChangedEvent,
        ref eventParameters,
        eventHandler,
        IntPtr.Zero,
        ref eventHandle,
        BLUETOOTH_GATT_FLAG_NONE
    );
}

我尝试使用设备句柄和从特征中获取的服务句柄作为第一个参数。改变这个并没有什么区别

c# winapi bluetooth-lowenergy
1个回答
0
投票

这是我的蓝牙框架的一部分,展示了如何使用旧版 GATT API 订阅特征更改通知。

private enum BTH_LE_GATT_EVENT_TYPE : uint
{
    CharacteristicValueChangedEvent
};

[StructLayout(LayoutKind.Sequential)]
private struct BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION
{
    [MarshalAs(UnmanagedType.U2)]
    public UInt16 NumCharacteristics;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    public BTH_LE_GATT_CHARACTERISTIC[] Characteristics;
};

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate void PFNBLUETOOTH_GATT_EVENT_CALLBACK(
    [param: MarshalAs(UnmanagedType.U4), In] BTH_LE_GATT_EVENT_TYPE EventType,
    [param: In, Out] ref BLUETOOTH_GATT_VALUE_CHANGED_EVENT EventOutParameter,
    [param: MarshalAs(UnmanagedType.SysInt), In] IntPtr Context);

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.I4)]
private delegate Int32 PBluetoothGATTRegisterEvent(
    [param: MarshalAs(UnmanagedType.SysInt), In] IntPtr hService,
    [param: MarshalAs(UnmanagedType.U4), In] BTH_LE_GATT_EVENT_TYPE EventType,
    [param: In, Out] ref BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION EventParameterIn,
    [param: MarshalAs(UnmanagedType.FunctionPtr), In] PFNBLUETOOTH_GATT_EVENT_CALLBACK Callback,
    [param: MarshalAs(UnmanagedType.SysInt), In] IntPtr CallbackContext,
    [param: MarshalAs(UnmanagedType.SysInt), Out] out IntPtr pEventHandle,
    [param: MarshalAs(UnmanagedType.U4), In] UInt32 Flags);

private Int32 GetDevicePath(Guid Service, out String Path)
{
    Path = "";
    Int32 Result = wclBluetoothErrors.WCL_E_BLUETOOTH_DEVICE_NOT_FOUND;
    
    IntPtr DevInfo = Setup.SetupDiGetClassDevs(Service, null, IntPtr.Zero, Setup.DIGCF_PRESENT | Setup.DIGCF_DEVICEINTERFACE);
    if (DevInfo == Common.INVALID_HANDLE_VALUE)
    {
        Result = DecodeError((UInt32)Marshal.GetLastWin32Error());
        if (Result == wclBluetoothErrors.WCL_E_BLUETOOTH_ACCESS_DENIED)
            return wclBluetoothErrors.WCL_E_BLUETOOTH_LE_ACCESS_DENIED;
        return Result;
    }
    
    String AddrStr = Address.ToString("X12").ToLower();
    
    UInt32 Ndx = 0;
    while (true)
    {
        Setup.SP_DEVICE_INTERFACE_DATA Data = new Setup.SP_DEVICE_INTERFACE_DATA();
        Data.cbSize = (UInt32)Marshal.SizeOf(typeof(Setup.SP_DEVICE_INTERFACE_DATA));
        if (!Setup.SetupDiEnumDeviceInterfaces(DevInfo, IntPtr.Zero, Service, Ndx, ref Data))
            break;
        
        UInt32 DetailsSize;
        IntPtr Details;
        Setup.SetupDiGetDeviceInterfaceDetail(DevInfo, ref Data, IntPtr.Zero, 0, out DetailsSize, IntPtr.Zero);
        if (Marshal.GetLastWin32Error() == Common.ERROR_INSUFFICIENT_BUFFER)
        {
            Details = wclHelpers.AllocHGlobal((Int32)DetailsSize); // PSP_DEVICE_INTERFACE_DETAIL_DATA
            if (Details != IntPtr.Zero)
            {
                Int32 StructSize = 6;
                if (IntPtr.Size == 8)
                    StructSize = 8; // On 64 bit structure has 8 bytes size.
                Marshal.WriteInt32(Details, StructSize);
                Boolean Res = Setup.SetupDiGetDeviceInterfaceDetail(DevInfo, ref Data, Details, DetailsSize, out DetailsSize, IntPtr.Zero);
                if (Res)
                {
                    IntPtr p = wclHelpers.IncPtr(Details, 4); // String always has 4 bytes offset.
                    String DevPath = Marshal.PtrToStringAuto(p);
                    DevPath = DevPath.ToLower();
                    if (DevPath.IndexOf(AddrStr) >= 0)
                    {
                        Path = DevPath;
                        Result = wclErrors.WCL_E_SUCCESS;
                    }
                }
                Marshal.FreeHGlobal(Details);
            }
        }
        if (Path != "")
            break;
        
        Ndx++;
    }
    Setup.SetupDiDestroyDeviceInfoList(DevInfo);
    
    return Result;
}

private Int32 OpenServiceHandle(wclGattOperationFlag Flag, UInt16 SvcHdl, wclGattService Service, out IntPtr DevHdl)
{
    DevHdl = Common.INVALID_HANDLE_VALUE;
    Guid Guid;
    if (Services[i].Uuid.IsShortUuid)
        Guid = new Guid("{0000" + Services.Uuid.ShortUuid.ToString("X4") + "-0000-1000-8000-00805F9B34FB}");
    else
        Guid = Services.Uuid.LongUuid;

    String Path;
    Result = GetDevicePath(Guid, out Path);
    if (Result != wclErrors.WCL_E_SUCCESS)
        return Result;

    // First try to open with read-write access.
    DevHdl = Io.CreateFile(Path, Io.GENERIC_READ | Io.GENERIC_WRITE, Io.FILE_SHARE_READ | Io.FILE_SHARE_WRITE, IntPtr.Zero, Io.OPEN_EXISTING, Io.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
    // If failed, try to open with only read access.
    if (DevHdl == Common.INVALID_HANDLE_VALUE)
        DevHdl = Io.CreateFile(Path, Io.GENERIC_READ, Io.FILE_SHARE_READ, IntPtr.Zero, Io.OPEN_EXISTING, Io.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
    // If failed again - return error.
    if (DevHdl == Common.INVALID_HANDLE_VALUE)
        return DecodeAttErrorCode(wclHelpers.HResultFromWin32(Marshal.GetLastWin32Error()));
    return wclErrors.WCL_E_SUCCESS;
}

protected override Int32 HalSubscribe(wclGattCharacteristic Characteristic, out IntPtr Hdl)
{
    Hdl = IntPtr.Zero;
    IntPtr Dev;
    Int32 Result = OpenServiceHandle(wclGattOperationFlag.goNone, Characteristic.ServiceHandle, out Dev);
    if (Result != wclErrors.WCL_E_SUCCESS)
        return Result;
    
    BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION Reg = new BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION();
    Reg.NumCharacteristics = 1;
    Reg.Characteristics = new BTH_LE_GATT_CHARACTERISTIC[1];
    Reg.Characteristics[0].ServiceHandle = Characteristic.ServiceHandle;
    Reg.Characteristics[0].AttributeHandle = Characteristic.Handle;
    Reg.Characteristics[0].CharacteristicValueHandle = Characteristic.ValueHandle;
    // On Windows 10 we need only handles. On Windows 8 and 8.1 we need all characteristic properties.
    Reg.Characteristics[0].CharacteristicUuid = UuidToGattUuid(Characteristic.Uuid);
    Reg.Characteristics[0].IsBroadcastable = Characteristic.IsBroadcastable;
    Reg.Characteristics[0].IsReadable = Characteristic.IsReadable;
    Reg.Characteristics[0].IsWritable = Characteristic.IsWritable;
    Reg.Characteristics[0].IsWritableWithoutResponse = Characteristic.IsWritableWithoutResponse;
    Reg.Characteristics[0].IsSignedWritable = Characteristic.IsSignedWritable;
    Reg.Characteristics[0].IsNotifiable = Characteristic.IsNotifiable;
    Reg.Characteristics[0].IsIndicatable = Characteristic.IsIndicatable;
    Reg.Characteristics[0].HasExtendedProperties = Characteristic.HasExtendedProperties;
    
    if (FCb == null)
        FCb = new PFNBLUETOOTH_GATT_EVENT_CALLBACK(GattCharChangeCallback);
    if (FCharChangeHdl == IntPtr.Zero)
        FCharChangeHdl = (IntPtr)GCHandle.Alloc(Receiver);
    
    IntPtr TmpHdl;
    return DecodeAttErrorCode(BluetoothGATTRegisterEvent(Dev, BTH_LE_GATT_EVENT_TYPE.CharacteristicValueChangedEvent, ref Reg, FCb, FCharChangeHdl, out TmpHdl, BLUETOOTH_GATT_FLAG_NONE));

}
© www.soinside.com 2019 - 2024. All rights reserved.