COM方法,Char类型和CharSet

时间:2014-07-30 14:20:57

标签: c# .net unicode com com-interop

这是我之前提问的后续问题: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的模拟(通常与DllImportAttributeStructLayoutAttribute一起使用)可以应用于COM接口方法。

然而,我没有得到"中文"在输出上。一切似乎工作正常,我从COM得到正确的Unicode字符。

对于COM方法互操作,是否Char始终被解释为WCHAR

我无法找到任何确认或否认这一点的文件。

1 个答案:

答案 0 :(得分:3)

我认为这是一个很好的问题,而charSystem.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[] buffushort只会转换为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 == 1DefaultCharSet没有任何影响。无法明确应用char。但是,您仍然可以使用CharSet完全控制每个参数的编组行为,其方式与上面的P / Invoke完全相同。例如,如果非托管COM代码需要ANSI字符的缓冲区,则您的MarshalAs方法可能如下所示:

Next