You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows平台下C++中如何等待ExNotifyCallback的回调响应?

Windows下实现回调等待超时的标准方案

Windows平台下最标准的做法是使用**事件对象(Event)**实现同步等待,配合上下文结构体传递状态与数据,具体实现如下:


1. 定义上下文结构体

用于存储同步事件、回调返回的数据以及请求唯一标识(区分多实例响应):

typedef struct _RESPONSE_CONTEXT {
    HANDLE hEvent;          // 同步事件句柄
    void* pReceivedData;    // 存储回调返回的数据
    int dataSize;           // 数据大小
    UINT requestId;         // 请求唯一标识,用于匹配对应实例的响应
} RESPONSE_CONTEXT, *PRESPONSE_CONTEXT;

2. 实现响应回调函数

在回调中校验请求标识,保存数据并触发事件通知等待线程:

void MyResponseFunc(void* pData, int* pDataSize) {
    PRESPONSE_CONTEXT pCtx = (PRESPONSE_CONTEXT)pData;
    // 替换为实际的请求ID校验逻辑,确保只处理当前等待的请求
    if (pCtx->requestId == GetCurrentActiveRequestId()) {
        // 拷贝回调返回的数据(需根据实际场景处理内存分配与释放)
        pCtx->pReceivedData = malloc(*pDataSize);
        if (pCtx->pReceivedData != NULL) {
            memcpy(pCtx->pReceivedData, pData, *pDataSize);
            pCtx->dataSize = *pDataSize;
        }
        // 触发事件,通知等待线程响应已到达
        SetEvent(pCtx->hEvent);
    }
}

3. 主流程实现

调用ExNotifyCallback后等待事件触发,处理超时与成功场景:

BOOL SendRequestAndWait(void* pIn1, void* pIn2, void** ppOutData, int* pOutSize, DWORD timeoutMs) {
    BOOL result = FALSE;
    RESPONSE_CONTEXT ctx = {0};
    UINT currentRequestId = GenerateUniqueRequestId(); // 生成唯一请求ID

    // 创建自动重置事件(触发后自动回到未触发状态,避免重复响应)
    ctx.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (ctx.hEvent == NULL) {
        return FALSE;
    }

    ctx.requestId = currentRequestId;
    // 将当前上下文绑定到响应回调(需根据实际场景实现,比如通过线程局部存储或ExNotifyCallback参数传递)
    BindResponseContext(&ctx);

    // 发送通知到监听实例
    ExNotifyCallback(myCbObject, pIn1, pIn2);

    // 等待事件触发或超时
    DWORD waitResult = WaitForSingleObject(ctx.hEvent, timeoutMs);
    switch (waitResult) {
        case WAIT_OBJECT_0:
            // 响应已到达,返回数据
            *ppOutData = ctx.pReceivedData;
            *pOutSize = ctx.dataSize;
            result = TRUE;
            break;
        case WAIT_TIMEOUT:
            // 超时,清理已分配内存
            if (ctx.pReceivedData != NULL) {
                free(ctx.pReceivedData);
            }
            result = FALSE;
            break;
        default:
            // 等待出错,清理资源
            if (ctx.pReceivedData != NULL) {
                free(ctx.pReceivedData);
            }
            result = FALSE;
            break;
    }

    // 清理资源
    CloseHandle(ctx.hEvent);
    UnbindResponseContext(); // 解除上下文绑定
    return result;
}

关键注意事项

  • 线程安全:多实例并发场景下,必须通过requestId严格匹配请求与响应,避免不同请求的回调互相干扰。
  • 内存管理:回调中分配的内存需在主流程中正确释放,防止内存泄漏。
  • 上下文传递:需确保响应回调能获取到当前请求的RESPONSE_CONTEXT,可通过线程局部存储(TLS)、ExNotifyCallback参数传递或带锁的全局字典实现。
  • 事件类型:使用自动重置事件可确保每次等待仅响应一次回调,避免后续误触发。

内容的提问来源于stack exchange,提问作者LalaWootwoot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 11:33:21