如何解决 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保留它,等到最后才回收。一般,出问题的的代码如下:

///


/// 调用 DLL 的方法(参数为回调函数地址)
///

private void CallDll()
{
    DllMethodA(OnCallBack);            
}

///


/// 给 DLL 的回调函数
///

///
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();

///


/// 调用 DLL 的方法(参数为回调函数地址)
///

private void CallDll()
{
    // 让 GC 保留,不回收这个回调函数
    _callbackFunc = new MethodCallbackEvent(OnCallBack);
    _gcHandle = GCHandle.Alloc(_callbackFunc);

    DllMethodA(_callbackFunc);
}

///


/// 给 DLL 的回调函数
///

///
private void OnCallBack(int retValue)
{
    Console.WriteLine($@"the return value from the dll is {retValue}");
}

///


/// 增加一个释放的方法,在合适的时候调用
///

private void ReleaseCallback()
{
    if (_gcHandle.IsAllocated) { _gcHandle.Free(); }
}

你可能感兴趣的:(c#,开发语言)