如何解决 C# 调用C/C++ DLL 回调函数导致内存崩溃的问题

问题描述:

1. C/C++ 写的 DLL,其中有个方法(如MethodA)有的参数是回调函数。

2. C# 调用 DLL的方法MethodA,将自己的一个内部方法(InnerMethod)作为回调函数的地址传递过去。

3. 当 InnerMethod 被触发时,往往第一次能成功,再次触发时就会崩溃,报的错误为非法内存访问之类。

碰到以上错误,估计你的大脑也会像内存一样:奔溃。可能会尝试各种办法:修改 C/C++ 代码,将方法加上 _stdcall;修改 C# 代码,调整调用约定;修改 InnerMethod,增加各种调试,改用临时变量,改用全局变量,只做计算,不访问其它资源。。。最后呢,还是奔溃。

我当时就被这个问题折磨了好几天,而同事的 QT 调用 DLL 却正常,这说明问题还是出在 C# 上。后来经过多方查找,发现是 C# 的GC的问题,是GC把C#内的回调函数回收了,当下次DLL再次调用这个函数时,就会触发非法内存访问的错误。

如何修复呢?强制GC保留它,等到最后才回收。一般,出问题的的代码如下:

/// <summary>
/// 调用 DLL 的方法(参数为回调函数地址)
/// </summary>
private void CallDll()
{
    DllMethodA(OnCallBack);            
}

/// <summary>
/// 给 DLL 的回调函数
/// </summary>
/// <param name="retValue"></param>
private void OnCallBack(int retValue)
{
    Console.WriteLine($@"the return value from the dll is {retValue}");
}

将代码修改如下:

// 声明委托,
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void MethodCallbackEvent(int i);
private static MethodCallbackEvent _callbackFunc = null;
private static GCHandle _gcHandle = new GCHandle();

/// <summary>
/// 调用 DLL 的方法(参数为回调函数地址)
/// </summary>
private void CallDll()
{
    // 让 GC 保留,不回收这个回调函数
    _callbackFunc = new MethodCallbackEvent(OnCallBack);
    _gcHandle = GCHandle.Alloc(_callbackFunc);

    DllMethodA(_callbackFunc);
}

/// <summary>
/// 给 DLL 的回调函数
/// </summary>
/// <param name="retValue"></param>
private void OnCallBack(int retValue)
{
    Console.WriteLine($@"the return value from the dll is {retValue}");
}

/// <summary>
/// 增加一个释放的方法,在合适的时候调用
/// </summary>
private void ReleaseCallback()
{
    if (_gcHandle.IsAllocated) { _gcHandle.Free(); }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值