我需要将包含C#音频流的缓冲区传递给本机dll。缓冲区驻留在struct中。缓冲区最好通过接口和接口传递。不是通过磁盘路径。我见过这种方法:
// native
struct MyStruct
{
short* buffer
}
void Foo(MyStruct *myStruct);
// managed
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=1000)]
public short[] buffer;
}
[DllImport("My.dll")]
public static extern void Foo(ref MyStruct myStruct);
第一个问题是这个代码是否适用于短*大小高达1K短路的缓冲区?
其次,提前未知大小:我是否需要在SizeConst中设置最大大小(可能是几MB)?
答案 0 :(得分:2)
首先,你问题中的两个结构不匹配。 C#结构匹配
struct MyStruct
{
short arr[1000];
};
这就是ByValArray
的意思 - 在结构中内联分配的数组。
如果大小是动态的(仅在运行时已知),那么您可能不应期望让编组人员为您处理此问题。你当然不希望每次都强制编组到一个恒定大小的缓冲区,因为这样效率很低。事实上,你真的想避免复制缓冲区。并且p / invoke封送程序对它准备编组的对象的大小有一个上限。
手动固定阵列并传递其地址会更加清晰和高效。您还应该传递数组的长度,以便C ++代码知道预期读取的数量。
在C ++方面:
struct BufferStruct
{
int len;
short* arr;
};
void Foo(const BufferStruct buffer);
在C#方面:
[StructLayout(LayoutKind.Sequential)]
public struct BufferStruct
{
public int len;
public IntPtr arr;
}
[DllImport("My.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Foo(BufferStruct buffer);
然后你调用这样的函数:
short[] arr = ...;
GCHandle gch = GCHandle.Alloc(arr, GCHandleType.Pinned);
try
{
BufferStruct buffer;
buffer.len = buffer.Length;
buffer.arr = gch.AddrOfPinnedObject();
Foo(buffer);
}
finally
{
gch.Free();
}
如果您没有将数组强制转换为结构,那么所有这些都会更容易。如果您将长度和数组作为参数传递,那么编组器会为您固定数组并使代码更简单。