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

C++ PCIe板卡DLL的C#封装:中断监听与跨语言事件触发咨询

问题解答

1. 持续监听板卡中断:用工作线程替代主线程无限循环

直接在主线程写无限循环会阻塞整个程序的UI或业务逻辑,正确方案是在C++ DLL中创建独立工作线程,线程内部通过WaitForSingleObject循环等待中断事件。

核心实现逻辑:

  • 在DLL中新增启动监听的导出接口,负责创建工作线程,并传入中断事件句柄、回调函数指针及退出信号事件。
  • 工作线程内循环流程:
    1. 同时监听退出信号和中断事件,优先响应退出指令
    2. 若捕获到中断事件(WaitForSingleObject返回WAIT_OBJECT_0),则处理中断逻辑并调用回调通知C#
    3. 重置手动中断事件(因你创建的是手动重置事件,触发后会保持信号态,必须调用ResetEvent才能继续等待下一次中断)
    4. 若未收到退出信号,回到步骤1继续等待

C++ DLL核心代码示例:

#include <windows.h>

// 定义回调函数类型,用于通知C#中断触发
typedef void(__stdcall *InterruptCallback)(void);

// 线程参数结构体:保存中断事件、回调、退出信号
typedef struct {
    HANDLE hInterruptEvent;
    InterruptCallback callback;
    HANDLE hQuitEvent;
} ThreadParams;

// 工作线程函数
DWORD WINAPI InterruptListenThread(LPVOID lpParam) {
    ThreadParams* pParams = (ThreadParams*)lpParam;
    HANDLE hEvents[2] = { pParams->hQuitEvent, pParams->hInterruptEvent };

    while (true) {
        // 同时等待退出信号和中断事件,INFINITE表示无超时等待
        DWORD dwWaitResult = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
        
        if (dwWaitResult == WAIT_OBJECT_0) {
            // 收到退出信号,终止线程
            break;
        } else if (dwWaitResult == WAIT_OBJECT_0 + 1) {
            // 触发中断,调用回调通知C#
            if (pParams->callback != NULL) {
                pParams->callback();
            }
            // 重置中断事件,准备下一次等待
            ResetEvent(pParams->hInterruptEvent);
        }
    }

    delete pParams;
    return 0;
}

// 导出接口:启动中断监听
extern "C" __declspec(dllexport) HANDLE __stdcall StartInterruptListen(HANDLE hInterruptEvent, InterruptCallback callback) {
    ThreadParams* pParams = new ThreadParams();
    pParams->hInterruptEvent = hInterruptEvent;
    pParams->callback = callback;
    // 创建退出事件,用于优雅终止线程
    pParams->hQuitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    // 创建工作线程
    return CreateThread(NULL, 0, InterruptListenThread, pParams, 0, NULL);
}

// 导出接口:停止中断监听
extern "C" __declspec(dllexport) void __stdcall StopInterruptListen(HANDLE hThread, HANDLE hQuitEvent) {
    SetEvent(hQuitEvent);
    // 等待线程退出,超时5秒强制结束
    WaitForSingleObject(hThread, 5000);
    // 释放资源
    CloseHandle(hThread);
    CloseHandle(hQuitEvent);
}

2. 高效触发C#事件:通过委托回调实现

C#可通过委托将事件处理函数传递给C++ DLL,当中断触发时,C++直接调用该委托的函数指针,从而触发C#中的事件,这是跨语言回调最高效的方式之一。

关键注意事项:

  • 委托签名必须与C++回调函数类型完全匹配(调用约定为__stdcall)
  • 必须固定委托内存,避免被.NET GC回收,否则会导致程序崩溃

C#核心代码示例:

using System;
using System.Runtime.InteropServices;

public class PcieCardListener
{
    // 定义与C++回调匹配的委托
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate void InterruptCallback();

    // 导入C++ DLL接口
    [DllImport("YourPcieDll.dll", CallingConvention = CallingConvention.StdCall)]
    private static extern IntPtr StartInterruptListen(IntPtr hInterruptEvent, InterruptCallback callback);

    [DllImport("YourPcieDll.dll", CallingConvention = CallingConvention.StdCall)]
    private static extern void StopInterruptListen(IntPtr hThread, IntPtr hQuitEvent);

    // 固定委托,防止GC回收
    private static GCHandle _callbackHandle;
    private static InterruptCallback _interruptCallback;
    private IntPtr _listenThreadHandle;
    private IntPtr _quitEventHandle;

    // 对外暴露的C#中断事件
    public event EventHandler InterruptReceived;

    public void StartListening(IntPtr hInterruptEvent)
    {
        // 初始化委托并固定内存
        _interruptCallback = OnInterruptTriggered;
        _callbackHandle = GCHandle.Alloc(_interruptCallback);

        // 启动监听线程(实际需根据DLL实现调整退出事件句柄的获取逻辑)
        _listenThreadHandle = StartInterruptListen(hInterruptEvent, _interruptCallback);
    }

    public void StopListening()
    {
        if (_listenThreadHandle != IntPtr.Zero)
        {
            StopInterruptListen(_listenThreadHandle, _quitEventHandle);
            _listenThreadHandle = IntPtr.Zero;
        }

        // 释放委托的固定内存
        if (_callbackHandle.IsAllocated)
        {
            _callbackHandle.Free();
        }
    }

    // 委托对应的处理函数,触发C#事件
    private void OnInterruptTriggered()
    {
        // 若涉及UI操作,需切换到UI线程(WinForms用Invoke,WPF用Dispatcher.Invoke)
        InterruptReceived?.Invoke(this, EventArgs.Empty);
    }
}

额外注意点

  • 事件重置:因你创建的是手动重置事件(CreateEvent第二个参数为TRUE),中断触发后必须调用ResetEvent,否则WaitForSingleObject会一直返回成功,导致无限触发回调。
  • 线程安全:C#事件触发若涉及UI更新,必须切换到UI线程执行,避免跨线程操作异常。
  • 资源清理:程序退出或停止监听时,必须调用StopInterruptListen释放线程和事件句柄,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:40:25