P / Invoke 属性是否可选用于编组数组?

假设存在一个带有纯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声明中省略它们吗?

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

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

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

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