問題描述:
1. C/C++ 寫的 DLL,其中有個方法(如MethodA)有的參數(shù)是回調(diào)函數(shù)。
2. C# 調(diào)用 DLL的方法MethodA,將自己的一個內(nèi)部方法(InnerMethod)作為回調(diào)函數(shù)的地址傳遞過去。
3. 當 InnerMethod 被觸發(fā)時,往往第一次能成功,再次觸發(fā)時就會崩潰,報的錯誤為非法內(nèi)存訪問之類。
碰到以上錯誤,估計你的大腦也會像內(nèi)存一樣:奔潰??赡軙L試各種辦法:修改 C/C++ 代碼,將方法加上 _stdcall;修改 C# 代碼,調(diào)整調(diào)用約定;修改 InnerMethod,增加各種調(diào)試,改用臨時變量,改用全局變量,只做計算,不訪問其它資源。。。最后呢,還是奔潰。
我當時就被這個問題折磨了好幾天,而同事的 QT 調(diào)用 DLL 卻正常,這說明問題還是出在?C# 上。后來經(jīng)過多方查找,發(fā)現(xiàn)是 C# 的GC的問題,是GC把C#內(nèi)的回調(diào)函數(shù)回收了,當下次DLL再次調(diào)用這個函數(shù)時,就會觸發(fā)非法內(nèi)存訪問的錯誤。
如何修復呢?強制GC保留它,等到最后才回收。一般,出問題的的代碼如下:
///
/// 調(diào)用 DLL 的方法(參數(shù)為回調(diào)函數(shù)地址)
///
private void CallDll()
{
?DllMethodA(OnCallBack); ? ? ? ? ? ?
}
///
/// 給 DLL 的回調(diào)函數(shù)
///
///
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();
///
/// 調(diào)用 DLL 的方法(參數(shù)為回調(diào)函數(shù)地址)
///
private void CallDll()
{
?// 讓 GC 保留,不回收這個回調(diào)函數(shù)
?_callbackFunc = new MethodCallbackEvent(OnCallBack);
?_gcHandle = GCHandle.Alloc(_callbackFunc);
?DllMethodA(_callbackFunc);
}
///
/// 給 DLL 的回調(diào)函數(shù)
///
///
private void OnCallBack(int retValue)
{
?Console.WriteLine($@"the return value from the dll is {retValue}");
}
///
/// 增加一個釋放的方法,在合適的時候調(diào)用
///
private void ReleaseCallback()
{
?if (_gcHandle.IsAllocated) { _gcHandle.Free(); }
}
你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧