如何从C#向C++ DLL传递数组末尾的指针引用?
解决C#调用ryg_rans时传递数组末尾指针的问题
1. 修正DllImport函数封装
你的C#封装中第二个参数类型错误:C++函数的uint32_t** pptr是指针的指针,用于在函数内部修改指针本身的值(让指针向前移动),而你当前用的ref uint是值类型的引用,完全不匹配。需要将参数类型改为ref IntPtr,因为IntPtr是C#中用于表示非托管内存指针的类型:
[DllImport(DllPath, EntryPoint = "Rans64EncPutSymbol")] public static extern void Rans64EncPutSymbol( ref Rans64State r, ref IntPtr pptr, ref Rans64EncSymbol sym, uint scaleBits = DEFAULT_SCALE_BITS); // 同步修正Rans64EncFlush的封装(若之前定义有误) [DllImport(DllPath, EntryPoint = "Rans64EncFlush")] public static extern void Rans64EncFlush(ref Rans64State r, ref IntPtr pptr);
2. 正确处理托管数组与指针传递
C#的托管数组会被GC自动管理,直接传递数组元素的引用无法满足需求,必须固定数组内存并获取其非托管指针,再计算出数组末尾的指针:
uint[] outputBuffer = new uint[OUT_MAX_ELEMS]; Rans64State ransState = new Rans64State(); RansWrapper.Rans64EncInit(ref ransState); // 固定托管数组,防止GC移动或回收它 GCHandle bufferHandle = GCHandle.Alloc(outputBuffer, GCHandleType.Pinned); try { // 获取数组的起始非托管指针 IntPtr bufferStart = bufferHandle.AddrOfPinnedObject(); // 计算数组末尾的指针:起始指针 + 元素数量 * 单个uint的字节数(4字节) IntPtr ptr = bufferStart + outputBuffer.Length * sizeof(uint); // 反向遍历输入数据,调用编码函数 for(int i = input.Length; i > 0; i--) { int s = input[i - 1]; // 传递ptr的引用,函数会修改ptr的值(向前移动) RansWrapper.Rans64EncPutSymbol(ref ransState, ref ptr, ref esyms[s]); } // 刷新编码器,同样传递ptr的引用 RansWrapper.Rans64EncFlush(ref ransState, ref ptr); // 此时ptr指向编码后数据的起始位置,可计算有效数据长度 int encodedLength = (int)((bufferStart + outputBuffer.Length * sizeof(uint) - ptr) / sizeof(uint)); } finally { // 必须释放GCHandle,避免内存泄漏 bufferHandle.Free(); }
关键说明
- 固定数组内存:用
GCHandle.Alloc固定托管数组后,GC不会再移动该数组的内存位置,确保指针始终有效。 - 指针计算:数组末尾指针的计算逻辑和C完全一致:
起始指针 + 元素总数 * 元素字节数,对应C中out_buf + out_max_elems的地址偏移逻辑。 - 传递指针的引用:用
ref IntPtr传递指针,对应C++的uint32_t**,这样函数内部修改指针的值时,C#中的ptr变量会同步更新。
内容的提问来源于stack exchange,提问作者Aenye_Cerbin
相关产品推荐
相关产品推荐

