P / Invoke [In,Out]属性是否可选用于编组数组?

时间:2013-01-16 19:19:21

标签: c# c++ arrays attributes pinvoke

假设存在一个带有纯C接口的本机函数,如下所示,从本机DLL导出:

// NativeDll.cpp

extern "C" void __stdcall FillArray(
    int fillValue, 
    int count, 
    int* data)
{
    // Assume parameters are OK...

    // Fill the array
    for (int i = 0; i < count; i++)
    {
        data[i] = fillValue;
    }
}

以下P / Invoke工作正常(使用VS2010 SP1测试):

[DllImport("NativeDll.dll", CallingConvention=CallingConvention.StdCall)]
public static extern void FillArray(
    int fillValue,
    int count,
    [In, Out] int[] data
);

以及此P / Invoke,与上述相同,但没有 [In, Out]属性

[DllImport("NativeDll.dll", CallingConvention=CallingConvention.StdCall)]
public static extern void FillArray(
    int fillValue,
    int count,
    int[] data
);

那么,编组数组的那些[In, Out]属性是否可选? 他们的目的是什么,如果有的话? 可以在我们的P / Invoke声明中省略它们吗?

1 个答案:

答案 0 :(得分:20)

不,他们不是完全可选。它碰巧偶然工作。然而,这是一个非常常见的事故。它的工作原理是因为数组实际上没有被编组。 pinvoke marshaller看到C#数组已经与本机数组兼容,因此跳过创建它的副本的步骤。它只是固定数组并将指针传递给本机代码。

这当然非常有效,您将不可避免地得到结果,因为本机代码直接写入数组元素。因此[In]和[Out]属性都不重要。

如果数组元素类型不那么简单,那就会变得更加模糊。在编组之后识别不是blittable或布局不匹配的结构或类类型的元素类型并不容易,因此pinvoke marshaller 具有来制作数组的副本。特别是布局不兼容性可能非常难以识别,因为托管布局是不可发现的。并且可以根据使用的抖动而改变。它可以在x86中工作但不能在x64中工作,当选择AnyCPU时非常讨厌。将修改后的副本复制回C#数组 需要[Out]。

不知道该建议是什么,除了没有人因为他们的声明中明确而被解雇。当数组元素类型不简单时,你应该总是明确的,这样你就不会发生意外。