You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不复制的情况下将C#的byte[]段传递给C++ DLL

解决方案:无复制传递byte[]切片给C++ DLL

核心目标是跳过数组复制操作,直接将原byte[]的指定切片段传递给非托管DLL,以下是几种可行方案:

方案1:直接传递原数组+偏移量(最简实现)

既然当前DLL函数接收数组和长度,可直接传递原数组packet.Payload,同时把切片的起始偏移和有效长度告知DLL——前提是调整C#委托签名,让它支持接收偏移参数:

修改后的C#委托:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate int ProcessVideoFrame_t(IntPtr bytesPtr, uint offset, uint len);

调用时无需复制数组,直接获取原数组的非托管指针:

// 固定原数组,防止GC在调用期间移动内存
fixed (byte* rawPtr = packet.Payload)
{
    IntPtr sliceStartPtr = new IntPtr(rawPtr + headerSize);
    uint sliceLength = (uint)(packet.Payload.Length - headerSize);
    helper.ProcessVideoFrame(CookieHelperDictionary[packet.Header.Cookie], sliceStartPtr, sliceLength);
}

注意:如果DLL会长期持有指针,不能用fixed,需改用GCHandle.Alloc(packet.Payload, GCHandleType.Pinned)固定数组,使用完毕后必须调用GCHandle.Free()释放。

方案2:用Span获取切片指针(.NET Core/.NET 5+)

在较新的.NET版本中,Span<byte>可以直观表示切片范围,并直接获取底层指针:

Span<byte> slice = packet.Payload.AsSpan(headerSize);
fixed (byte* slicePtr = slice)
{
    uint sliceLen = (uint)slice.Length;
    // 委托参数需改为IntPtr,Span无法直接被封送
    helper.ProcessVideoFrame(..., new IntPtr(slicePtr), sliceLen);
}

本质和方案1一致,但用Span更清晰地表达切片语义。

关于ArraySegment、Memory的说明

  • ArraySegment<byte>只是原数组的包装类,仅存储原数组、偏移和长度,不复制数据。但它无法直接通过UnmanagedType.LPArray封送,需从中取出原数组和偏移,再用方案1的方式传递。
  • Memory<T>是托管内存的抽象,对于数组来说底层仍是原数组。但Memory<T>不能直接获取指针,需先转为Span<T>(通过memory.Span),再用fixed取指针,逻辑同方案2。

关键提醒:为什么不能直接用ArraySegment/Span作为LPArray参数?

UnmanagedType.LPArray的封送逻辑是取数组的起始地址,不会识别切片的偏移量。直接传递ArraySegment/Span给标注该特性的参数,会导致DLL拿到整个原数组的起始地址,而非切片的起始位置,不符合需求。

总结

如果能调整DLL对应的委托签名,传递原数组指针+偏移+长度是最优解,完全避免复制开销。若无法修改签名,只能保留当前的Array.Copy方式,因为LPArray封送仅支持完整数组。

内容的提问来源于stack exchange,提问作者Meme Machine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 00:10:42