托管C#到非托管C++代码回调异常:返回垃圾值问题排查
C#托管代码调用带回调的非托管C++代码返回垃圾值问题
我用C#托管代码调用包含回调逻辑的非托管C++代码,当前初始化委托的C#代码运行后返回垃圾值,无法确认代码是否正常工作。
现有C#代码
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace ConnectToScriptEngineDll { public class ConnectToScriptEngine { public unsafe delegate float myCallBackPtr(float** tagValue1); [DllImport("LTscriptenginedll.dll", CallingConvention = CallingConvention.StdCall)] public static extern unsafe int ConnectLiveData(myCallBackPtr ps, IntPtr* test, int** temp, bool bdiscconectConnection, bool bPyRequest); public unsafe float StartLiveInfo(string tagNameFromRequest) { string tagName = tagNameFromRequest;//"Motor_1.OutPut.VI"; byte[] tagNameByte = Encoding.ASCII.GetBytes(tagName); IntPtr tagNamePtr = Marshal.UnsafeAddrOfPinnedArrayElement(tagNameByte, 0); IntPtr[] tagNameIntPtr = new IntPtr[2]; tagNameIntPtr[0] = tagNamePtr; tagNameIntPtr[1] = IntPtr.Zero; int tagValueTemp = 0; int*[] tagValuePtr = new int*[2]; tagValuePtr[0] = &tagValueTemp; tagValuePtr[1] = null; float test; fixed (int** tagValue1 = tagValuePtr) { fixed (IntPtr* tagName1 = tagNameIntPtr) { try { myCallBackPtr obj = new myCallBackPtr(CallMethod); var finalTagValue = ConnectLiveData(obj, tagName1, tagValue1, false, true); test = Convert.ToSingle(*tagValue1[0]); return test; } catch (Exception ex) { return 0; } } } } public unsafe static float CallMethod(float** tagValue1) { var test4 = Convert.ToSingle(*tagValue1[0]); Console.WriteLine("callbacked"); return test4; } } }
对应C++代码
USE_SE short ConnectLiveData(void* pCallBack, const char** pszTagNames, const int** pnTagAddress, bool bdiscconectConnection, bool bPyRequest) { short lnResult = -1; Lstring lsErrorMsg; if (NULL == g_pLiveDataChannel) { g_pLiveDataChannel = new CLiveDataChannel("", g_bIncomingLogEnableFlag, g_bOutgoingLogEnableFlag); g_nLiveDataChannelsCount++; } CSLCallInfo* lpcSLCall = new CSLCallInfo(pszTagNames, pnTagAddress, (onDPConnectdata)pCallBack, bPyRequest); if (lpcSLCall->m_nTagCount > 100) { lsErrorMsg = "Invalid number of parameters received for StartLiveCall TagName = " + lpcSLCall->m_sTagNamesKey; ST_LOG_DATA_BASE(ERROR_MSG, APP_NAME, (char*)lsErrorMsg.c_str()); delete lpcSLCall; lpcSLCall = NULL; return lnResult; } if (lpcSLCall->m_sTagNamesKey.empty()) { lsErrorMsg = "Failed start StartLiveCall - fiiled to create CSLCallInfo key : TagName = " + lpcSLCall->m_sTagNamesKey; ST_LOG_DATA_BASE(ERROR_MSG, APP_NAME, (char*)lsErrorMsg.c_str()); delete lpcSLCall; lpcSLCall = NULL; return lnResult; } lnResult = g_pLiveDataChannel->InitLiveDataCall(lpcSLCall, pszTagNames, pnTagAddress); if (lnResult >= 0) { g_mLiveDataChannelMutex.lock(); g_LiveDataChannelsList.insert(std::pair<Lstring, Lpair<CLiveDataChannel*, Lstring>> (lpcSLCall->m_sTagNamesKey, Lpair<CLiveDataChannel*, Lstring>(g_pLiveDataChannel, ""))); g_mLiveDataChannelMutex.unlock(); } else { delete lpcSLCall; lpcSLCall = NULL; } return lnResult; }
问题分析与修复方案
1. 委托类型不匹配
C中回调类型onDPConnectdata的参数与C#委托定义不一致:C#委托用float**,但C传递的pnTagAddress是const int**,回调实际接收的是整数指针数组,类型不匹配直接导致垃圾值。修正委托定义:
public unsafe delegate float myCallBackPtr(int** tagValue1);
2. 委托被GC回收风险
局部委托obj在ConnectLiveData调用后可能被GC回收,导致回调时指针失效。将委托存储为类成员变量延长生命周期:
private static myCallBackPtr _callbackDelegate; // 类成员存储委托 public unsafe float StartLiveInfo(string tagNameFromRequest) { // ... 其他代码 ... _callbackDelegate = new myCallBackPtr(CallMethod); var finalTagValue = ConnectLiveData(_callbackDelegate, tagName1, tagValue1, false, true); // ... 其他代码 ... }
3. 字符串内存未固定
tagNameByte是局部数组,未固定就获取指针,GC可能移动数组内存导致指针失效。用fixed语句固定数组,同时补充C++字符串需要的终止符:
byte[] tagNameByte = Encoding.ASCII.GetBytes(tagName + '\0'); // 补充字符串终止符 fixed (byte* tagNamePtr = tagNameByte) { IntPtr[] tagNameIntPtr = new IntPtr[2]; tagNameIntPtr[0] = (IntPtr)tagNamePtr; tagNameIntPtr[1] = IntPtr.Zero; // ... 后续代码放在fixed块内 ... }
4. 函数返回值类型不匹配
C++函数返回short,但C#定义为int,虽然能兼容但建议保持一致:
[DllImport("LTscriptenginedll.dll", CallingConvention = CallingConvention.StdCall)] public static extern unsafe short ConnectLiveData(myCallBackPtr ps, IntPtr* test, int** temp, bool bdiscconectConnection, bool bPyRequest);
5. 回调函数修正
回调参数改为int**后,转换逻辑对应调整:
public unsafe static float CallMethod(int** tagValue1) { var test4 = Convert.ToSingle(*tagValue1[0]); Console.WriteLine("callbacked"); return test4; }
内容的提问来源于stack exchange,提问作者Alok Sharma
相关产品推荐
相关产品推荐

