avo*_*avo 10 .net c# c++ com com-interop
我有这个COM方法签名,在C#中声明:
void Next(ref int pcch,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
char[] pchText);
Run Code Online (Sandbox Code Playgroud)
我称之为:
int cch = 100;
var buff = new char[cch];
com.Next(ref cch, buff);
Run Code Online (Sandbox Code Playgroud)
.NET互操作层是否首先将整个数组复制到临时的非托管内存缓冲区,然后将其复制回来?或者数组是否自动固定并通过引用传递?
为了尝试,我在COM对象(C++)中做到了这一点:
*pcch = 1;
pchText[0] = L'A';
pchText[1] = L'\x38F'; // '?'
Run Code Online (Sandbox Code Playgroud)
我'?'回来时检查buff[1]C#时会回来.但我不认为这是一个强有力的证据,证明阵列固定,而不是来回复制.
这并不总是很容易辨别,特别是当你使用无效声明时.char []不能作为LPWStr封送,它必须是LPArray.现在CharSet属性起作用,因为你没有指定它,char []将被编组为8位char [],而不是16位wchar_t [].编组的数组元素大小不同(它不是"blittable"),因此编组器必须复制数组.
非常不受欢迎,特别是考虑到您的C++代码需要wchar_t.在这个特定情况下,一个非常简单的方法是在数组中没有得到任何回报.如果通过复制对数组进行封送处理,则必须明确告诉编组程序在调用后需要将数组复制回来.您必须[In, Out]在参数上应用该属性.你会得到中文.
判断数组是否通过复制进行编组的常用方法是使用调试器.在C#程序中启用非托管调试.在调用中设置断点以及在本机函数的第一个语句中设置断点.当第一个断点点击时,使用Debug + Windows + Memory + Memory 1.将buff放入地址框并将显示切换为"4-byte Integer".您将看到数组对象的地址,4字节类型句柄,4字节数组长度和数组内容本身.因此,您知道如果未复制数组,则传递的地址是显示的地址加上8.
按F5继续,本机功能中的断点命中.查看pchText参数,调试器会告诉您它的地址.如果匹配则编组器只是传递一个指针.如果没有,那么你得到了一个数组的副本.
我们来做一个小实验.首先,让我们将您的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;
}
Run Code Online (Sandbox Code Playgroud)
然后,更改C#方法签名:
void Next(ref uint pcch, out IntPtr addr,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
char[] pbuff);
Run Code Online (Sandbox Code Playgroud)
最后,像这样测试:
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);
Run Code Online (Sandbox Code Playgroud)
正如所料,addr1 == addr2是true.因此,显然数组在传递给COM时会被固定而不是被复制.
也就是说,我找不到任何将此作为CLR实现的硬性要求的文档.例如,Mono可能会或可能不会这样.
| 归档时间: |
|
| 查看次数: |
538 次 |
| 最近记录: |