将GitHub上的LEPCC库移植到C#遇问题:DllImport错误与访问冲突
问题:C#调用Lepcc DLL出现访问冲突(内存损坏)
我正尝试将采用C调用约定的C库Lepcc移植到C#,移植示例应用时,程序结束会出现访问冲突(内存损坏),但ptVec的数据正确且与C示例一致。
定位到问题源于以下C++函数调用:
C++调用代码:
vector<Point3D> ptVec(nPts); errCode = (ErrCode)lepcc_decodeXYZ(ctx, &ptr, (int)(len - pos), &nPts, (double*)(&ptVec[0]));
对应的C++函数声明:
lepcc_status lepcc_decodeXYZ(lepcc_ContextHdl _ctx, const unsigned char** ppByte, int bufferSize, unsigned int* nPtsInOut, double* xyzBuffOut);
我在C#中的DllImport定义:
[DllImport(LepccDll, CallingConvention = CallingConvention.Cdecl)] public static extern lepcc_status lepcc_decodeXYZ(IntPtr ctx, ref IntPtr ppByte, int bufferSize, ref uint nPtsInOut, double[] xyzBuffOut);
C#调用代码:
var slpkBytes = System.IO.File.ReadAllBytes(fnIn); long slpkLength = new FileInfo(fnIn).Length; IntPtr ctx = LepccInterop.lepcc_createContext(); GCHandle slpkBytesPinned = GCHandle.Alloc(slpkBytes, GCHandleType.Pinned); IntPtr ptr = slpkBytesPinned.AddrOfPinnedObject() + pos; double[] ptVec = new double[(int)nPts * 3]; errCode = (ErrorCode)LepccInterop.lepcc_decodeXYZ(ctx, ref ptr, (int)(slpkLength - pos), ref nPts, ptVec); slpkBytesPinned.Free(); LepccInterop.lepcc_deleteContext(ctx);
请问我的DllImport写法是否正确?
分析与解决方案
你的DllImport整体方向正确,但存在两处关键问题,这是引发内存损坏的核心原因:
1. nPtsInOut参数的缓冲区匹配问题
C++函数中nPtsInOut是输入输出参数:输入你预分配的点数,输出实际解码得到的点数。你当前按初始nPts的3倍分配ptVec,如果函数返回的实际点数大于初始nPts,就会导致缓冲区溢出,直接引发内存访问冲突。
修正方案:
采用C库常见的“预查询+分配”模式:
// 第一步:调用函数获取实际需要的点数 uint actualPts = nPts; IntPtr tempPtr = IntPtr.Add(slpkBytesPinned.AddrOfPinnedObject(), pos); errCode = (ErrorCode)LepccInterop.lepcc_decodeXYZ(ctx, ref tempPtr, (int)(slpkLength - pos), ref actualPts, null); // 第二步:根据实际点数分配足够的缓冲区 double[] ptVec = new double[(int)actualPts * 3]; // 第三步:重置指针位置,再次调用完成解码 tempPtr = IntPtr.Add(slpkBytesPinned.AddrOfPinnedObject(), pos); errCode = (ErrorCode)LepccInterop.lepcc_decodeXYZ(ctx, ref tempPtr, (int)(slpkLength - pos), ref actualPts, ptVec);
2. 指针偏移的平台兼容性问题
你当前用slpkBytesPinned.AddrOfPinnedObject() + pos计算偏移,在32位平台下如果pos超过int范围会溢出,正确写法应使用IntPtr.Add方法保证跨平台兼容性:
IntPtr ptr = IntPtr.Add(slpkBytesPinned.AddrOfPinnedObject(), pos);
3. 额外潜在问题:缓冲区大小溢出
如果slpkLength - pos的值超过int类型最大值(32位平台为2^31-1),强制转换为int会导致溢出,传递错误的缓冲区大小给C函数。如果处理大文件,需要先检查数值范围,或者修改C函数的bufferSize参数类型为size_t,对应C#中使用uint或IntPtr。
内容的提问来源于stack exchange,提问作者chris crowe
相关产品推荐
相关产品推荐

