【发布时间】:2019-01-04 22:19:56
【问题描述】:
我有一个项目可以处理相当大的数组。有些代码是用 C 编写的,有些是用 C# 编写的。要将数据从 C 传递到 C#,我使用了 MarshalAs 属性。由于数组很大,我宁愿避免复制。所以我的问题是:MarshalAs 会导致数据从 C 复制到 C#,还是 C# byte[] 会引用 C 分配的完全相同的位置?
C 函数负责将数据传递给 C#,这意味着我使用回调。回调如下所示:
void (*to_call)(const uint8_t* buffer, int buffer_length);
DLLEXPORT
init(void(*callback)(const uint8_t* buffer, int buffer_length))
{
to_call = callback;
}
最终,to_call 回调将从 C 中调用。C# 代码如下所示:
public delegate void Callback([In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]byte[] buffer, int bufferLength);
[DllImport(DLLPath)]
public static extern void init(Callback callback);
我也想知道答案如何适用于 .Net Framework 和 .Net Core。
【问题讨论】:
-
我手头没有 Adam Nathan 的“.NET 和 COM”一书,但如果我没记错的话,“视情况而定”。同样,如果我记得(大约十年前),这取决于这两种表示是否是各向同性的(即,它们以相同的方式适合相同的位)。从 .NET
byte[]转到非托管代码时,它可能会起作用。相反的方向(我很确定)不起作用。 .NET 数组是以特定方式布局的托管对象。我大约 99% 确定您不能从非托管的字节集合中构建byte[]。新的Span功能可能会有所帮助?? -
您可以通过在托管内存 (C#) 中分配所有结构、固定它们并将指针传递给非托管代码来避免复制。 Rick Brewster 做了a good write-up (with code) 详细说明了他是如何在paint.net 中做到这一点的;还有see this article除此之外,您可能还想查看Memory Mapped files。
-
你是打算在 C# 中使用这些数组还是只是传递它们?如果您只是传递它们,您可以使用 IntPtr(对于 uint8_t*)。请注意,您还可以在需要时从 IntPtr 读取并复制它指向 byte[] 的位置。
标签: c# pinvoke marshalling