使用P-Invoke

时间:2015-07-09 12:05:31

标签: c# interop pinvoke

我需要将包含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)?

1 个答案:

答案 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();
}

如果您没有将数组强制转换为结构,那么所有这些都会更容易。如果您将长度和数组作为参数传递,那么编组器会为您固定数组并使代码更简单。