.NET互操作来回复制数组数据,还是固定数组?

我有这个COM方法签名,在C#中声明:

void Next(ref int pcch, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] char[] pchText); 

我称之为:

 int cch = 100; var buff = new char[cch]; com.Next(ref cch, buff); 

.NET互操作层是否首先将整个数组复制到临时的非托管内存缓冲区,然后将其复制回来? 或者数组是否自动固定并通过引用传递?

为了尝试,我在COM对象(C ++)中做到了这一点:

 *pcch = 1; pchText[0] = L'A'; pchText[1] = L'\x38F'; // 'Ώ' 

当我在返回时检查C#中的buff[1]时,我确实得到'Ώ' 。 但我不认为这是一个强有力的证据,certificatearrays固定,而不是来回复制。

我们来做一个小实验。 首先,让我们将您的COM方法更改为这样(在C ++中):

 STDMETHODIMP CComObject::Next(ULONG* pcch, int* addr, OLECHAR* pbuff) { pbuff[0] = L'A'; pbuff[1] = L'\x38F'; *addr = (int)pbuff; *pcch = 1; return S_OK; } 

然后,更改C#方法签名:

 void Next(ref uint pcch, out IntPtr addr, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] char[] pbuff); 

最后,像这样测试:

 uint cch = 10; var buff = new char[cch]; IntPtr addr1; unsafe { fixed (char* p = &buff[0]) { addr1 = (IntPtr)p; } } IntPtr addr2; com.Next(ref cch, out addr2, buff); Console.WriteLine(addr1 == addr2); 

正如所料, addr1 == addr2true 。 因此,显然数组在传递给COM时会被固定而不是被复制。

也就是说,我找不到任何将此作为CLR实现的硬性要求的文档。 例如,Mono可能会或可能不会这样。

这并不总是很容易辨别,特别是当你使用无效的声明时。 char []不能作为LPWStr封送,它必须是LPArray。 现在CharSet属性起作用,因为你没有指定它,char []将被编组为8位char [],而不是16位wchar_t []。 编组的数组元素大小不同(它不是“blittable”),因此编组器必须复制数组。

非常不受欢迎,特别是考虑到您的C ++代码需要wchar_t。 在这个特定情况下,一个非常简单的方法是在数组中没有得到任何回报。 如果通过复制对数组进行封送处理,则必须明确告诉编组程序在调用后需要将数组复制回来。 您必须在参数上应用[In, Out]属性。 你会得到中国人。

判断数组是否通过复制进行编组的常用方法是使用调试器。 在C#程序中启用非托管调试。 在调用中设置断点以及在本机函数的第一个语句中设置断点。 当第一个断点点击时,使用Debug + Windows + Memory + Memory 1.将buff放入地址框并将显示切换为“4字节整数”。 您将看到数组对象的地址,4字节类型句柄,4字节数组长度和数组内容本身。 因此,您知道如果未复制数组,则传递的地址是显示的地址加8。

按F5继续,本机function中的断点命中。 查看pchText参数,调试器会告诉您它的地址。 如果它匹配则编组器只是传递一个指针。 如果没有,那么你得到了一个数组的副本。