这是我之前提问的后续问题:Does .NET interop copy array data back and forth, or does it pin the array?
我的方法是一个COM接口方法(而不是DllImport
方法)。 C#签名如下所示:
void Next(ref int pcch,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
char [] pchText);
MSDN says:
当托管Char类型(默认情况下具有Unicode格式)时 传递给非托管代码,interop marshaler转换角色 设置为ANSI。您可以将DllImportAttribute属性应用于 平台调用声明和StructLayoutAttribute属性 到一个COM互操作声明来控制哪个字符集a marshaled Char类型使用。
另外,@ HansPassant在他的回答中here says:
char []无法封送为LPWStr,它必须是LPArray。现在 CharSet属性起作用,因为你没有指定它,所以 char []将被编组为8位char [],而不是16位wchar_t []。 封送的数组元素大小不同(不是 " blittable")所以编组必须复制数组。
非常不受欢迎,特别是考虑到您的C ++代码需要 wchar_t的。在这个特定情况下,一个非常简单的方法就是得到 阵中的任何东西。如果通过复制对数组进行封送处理 你必须明确地告诉marshaller数组需要 在通话结束后复制回来。你必须申请[In,Out] 参数的属性。你会得到中文。
我发现CharSet
的模拟(通常与DllImportAttribute
和StructLayoutAttribute
一起使用)可以应用于COM接口方法。
然而,我没有得到"中文"在输出上。一切似乎工作正常,我从COM得到正确的Unicode字符。
对于COM方法互操作,是否Char
始终被解释为WCHAR
?
我无法找到任何确认或否认这一点的文件。
答案 0 :(得分:3)
我认为这是一个很好的问题,而char
(System.Char
)互操作行为确实值得关注。
在托管代码中,sizeof(char)
始终等于2
(两个字节),因为在.NET中,字符始终是Unicode。
尽管如此,对于P / Invoke(调用导出的DLL API)和COM(调用COM接口方法)char
的情况,编组规则也有所不同。
对于P / Invoke ,CharSet
可以明确地与任何[DllImport]
属性一起使用,或通过[module|assembly: DefaultCharSet(CharSet.Auto|Ansi|Unicode)]
隐式使用,以更改所有{的默认设置{1}}每个模块或每个程序集的声明。
默认值为[DllImport]
,这意味着将进行Unicode到ANSI的转换。我使用CharSet.Ansi
将默认值更改为Unicode,然后在需要调用ANSI API的极少数情况下有选择地使用[module: DefaultCharSet(CharSet.Unicode)]
。
还可以使用[DllImport(CharSet = CharSet.Ansi)]
或char
(对于MarshalAs(UnmanagedType.U1|U2)
参数)更改任何特定的MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1|U2)
类型参数。例如,你可能有这样的事情:
char[]
在这种情况下,[DllImport("Test.dll", ExactSpelling = true, CharSet = CharSet.Unicode)]
static extern bool TestApi(
int length,
[In, Out, MarshalAs(UnmanagedType.LPArray] char[] buff1,
[In, Out, MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.U1)] char[] buff2);
将作为双字节值数组(按原样)传递,但buff1
将与单字节值数组进行转换。请注意,这仍然是buff2
的智能,Unicode到操作系统当前代码页(并返回)转换。例如,Unicode'\ x20AC'(buff2
)将在非托管代码中变为€
(操作系统代码页为\x80
)。这就是Windows-1252
的编组与MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1)] char[] buff
不同的方式。对于MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1)] ushort[] buff
,ushort
只会转换为0x20AC
。
对于调用COM接口方法,故事情况完全不同。在那里, 0xAC
始终被视为表示Unicode字符的双字节值。也许,Don Box的“Essential COM”(引用this page的脚注)暗示了这种设计决策的原因:
选择
char
类型以支持Win32 API使用的公共OLECHAR
数据类型,以减少支持每个接口的两个版本(TCHAR
和{{1}的需要}})。通过仅支持一种字符类型,对象开发人员与其客户端使用的UNICODE预处理器符号的状态分离。
显然,同样的概念已经进入.NET。即使对于传统的ANSI平台(如Windows 95,CHAR
),我也非常有信心。
请注意,当WCHAR
是COM接口方法签名的一部分时,Marshal.SystemDefaultCharSize == 1
对DefaultCharSet
没有任何影响。无法明确应用char
。但是,您仍然可以使用CharSet
完全控制每个参数的编组行为,其方式与上面的P / Invoke完全相同。例如,如果非托管COM代码需要ANSI字符的缓冲区,则您的MarshalAs
方法可能如下所示:
Next