如何将成员函数作为与 Windows 上的蓝牙相关的参数回调函数传递?

Posted

技术标签:

【中文标题】如何将成员函数作为与 Windows 上的蓝牙相关的参数回调函数传递?【英文标题】:How do I pass a member function as a parameter callback function with regards to Bluetooth on Windows? 【发布时间】:2018-07-03 20:05:30 【问题描述】:

我正在尝试使用 Windows 的蓝牙 API 从心率监视器获取心率,但我遇到了 BluetoothGATTRegisterEvent 函数的问题,即第四个参数需要一个 PFNBLUETOOTH_GATT_EVENT_CALLBACK 回调函数作为参数.执行以下代码可以正常工作:

// Function definition
void SomeEvent(BTH_LE_GATT_EVENT_TYPE EventType, PVOID EventOutParameter, PVOID Context)
 /* Function code */

// Calling BluetoothGATTRegisterEvent
hr = BluetoothGATTRegisterEvent(
                hLEDevice,
                EventType,
                &EventParameterIn,
                SomeEvent,
                NULL,
                &EventHandle,
                BLUETOOTH_GATT_FLAG_NONE);

但是,如果我尝试传递成员函数(例如 HeartRateMonitor::SomeEvent 而不仅仅是 SomeEvent),我会收到以下错误:

argument of type "void (HeartRateMonitor::*)(BTH_LE_GATT_EVENT_TYPE EventType, PVOID EventOutParameter, PVOID Context)" is incompatible with parameter of type "PFNBLUETOOTH_GATT_EVENT_CALLBACK"

我的第一直觉是尝试使用函数指针或 std::bind 对象,但这些都不起作用。将回调函数作为此参数传递时是否需要特别注意,或者我是否遗漏了一些关于成员函数的明显内容?

【问题讨论】:

c++: How to obtain context when callback doesn't provide user arg?的可能重复 【参考方案1】:

在函数名前加上“CALLBACK”:

// Function definition
void CALLBACK SomeEvent(BTH_LE_GATT_EVENT_TYPE EventType, PVOID EventOutParameter, PVOID Context)
 /* Function code */

它对我有用(VC 2019 和 mingw)。

【讨论】:

【参考方案2】:

这就是 BluetoothGATTRegisterEvent's CallbackContext 参数的用途。

必须将static 成员函数作为回调传递,this 作为CallbackContext。非静态成员函数有一个隐式 this 作为第一个参数 (__thiscall),不能用作回调。

在回调中,您可以将 Context 强制转换为 this 并访问您班级的其他成员。

例子:

class HeartRateMonitor 
public:
    //...
    void MemberFun(BTH_LE_GATT_EVENT_TYPE EventType, PVOID EventOutParameter, PVOID Context)
    

    static void StaticMemberFun(BTH_LE_GATT_EVENT_TYPE EventType, PVOID EventOutParameter, PVOID Context)
    
        HeartRateMonitor* pThis = static_cast<HeartRateMonitor*>(Context);

        /* Function code */
        // and here you could access your class non-static data by pThis->...
        // or call a non-static method, so you don't need to always writhe `pThis`
    

    HeartRateMonitor()
    
        HANDLE hLEDevice = 0;
        BTH_LE_GATT_EVENT_TYPE EventType = CharacteristicValueChangedEvent;
        PVOID EventParameterIn = 0;
        BLUETOOTH_GATT_EVENT_HANDLE EventHandle = 0;
        HRESULT hr = 0;

        hr = ::BluetoothGATTRegisterEvent(  // ok
                hLEDevice,
                EventType,
                &EventParameterIn,
                &HeartRateMonitor::StaticMemberFun,
                this,
                &EventHandle,
                BLUETOOTH_GATT_FLAG_NONE);

        hr = ::BluetoothGATTRegisterEvent(  // error C2664: 'unsigned long BluetoothGATTRegisterEvent(HANDLE,BTH_LE_GATT_EVENT_TYPE,void *,PFNBLUETOOTH_GATT_EVENT_CALLBACK,void *,BLUETOOTH_GATT_EVENT_HANDLE *,unsigned long)': cannot convert argument 4 from 'void (__cdecl HeartRateMonitor::* )(BTH_LE_GATT_EVENT_TYPE,void *,void *)' to 'PFNBLUETOOTH_GATT_EVENT_CALLBACK'
                hLEDevice,
                EventType,
                &EventParameterIn,
                &HeartRateMonitor::MemberFun,
                this,
                &EventHandle,
                BLUETOOTH_GATT_FLAG_NONE);
    
;

Example

【讨论】:

我遇到了同样的问题,但是我无法用你的建议解决这个问题,你能否给出具体的例子@Mihayl,因为我无法尝试什么我面临同样的错误,在问题

以上是关于如何将成员函数作为与 Windows 上的蓝牙相关的参数回调函数传递?的主要内容,如果未能解决你的问题,请参考以下文章

如何将元数据从 ExoPlayer 发送到蓝牙?

如何在windows pc和android设备之间建立蓝牙连接

如何将一个类成员函数与param作为rvalue绑定到boost :: function? [关闭]

如何修复与 MacOSX 上的全局命名空间错误中没有成员相关的缺失时间?

如何将 Android 应用程序正确连接到支持蓝牙的 Arduino 微控制器上的 RFCOMM 插座?

JS高级——静态成员与实例成员