gpt4 book ai didi

c# - COM 方法、Char 类型和 CharSet

转载 作者:行者123 更新时间:2023-11-30 22:04:40 25 4
gpt4 key购买 nike

这是我之前问题的后续问题:Does .NET interop copy array data back and forth, or does it pin the array?

我的方法是 COM 接口(interface)方法(而不是 DllImport 方法)。 C# 签名如下所示:

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

MSDN says :

When a managed Char type, which has Unicode formatting by default, is passed to unmanaged code, the interop marshaler converts the character set to ANSI. You can apply the DllImportAttribute attribute to platform invoke declarations and the StructLayoutAttribute attribute to a COM interop declaration to control which character set a marshaled Char type uses.

此外,@HansPassant 在他的回答中 here says :

A char[] can't be marshaled as LPWStr, it has to be LPArray. Now the CharSet attribute plays a role, since you did not specify it, the char[] will be marshaled as an 8-bit char[], not a 16-bit wchar_t[]. The marshaled array element is not the same size (it is not "blittable") so the marshaller must copy the array.

Pretty undesirable, particularly given that your C++ code expects wchar_t. A very easy way to tell in this specific case is not getting anything back in the array. If the array is marshaled by copying then you have to tell the marshaller explicitly that the array needs to be copied back after the call. You'd have to apply the [In, Out] attribute on the argument. You'll get Chinese.

我找不到 CharSet 的类似物(通常与 DllImportAttributeStructLayoutAttribute 一起使用)可以应用于 COM 接口(interface)方法。

然而,我没有在输出中得到“中文”。一切似乎都运行良好,我确实从 COM 返回了正确的 Unicode 字符。

这是否意味着对于 COM 方法互操作,Char 总是被解释为 WCHAR

我找不到任何证实或否认这一点的文件。

最佳答案

我认为这是一个很好的问题,char (System.Char) 互操作行为确实值得关注。

在托管代码中,sizeof(char) 始终等于 2(两个字节),因为在 .NET 中字符始终是 Unicode。

然而,当 char 用于 P/Invoke(调用导出的 DLL API)和 COM(调用 COM 接口(interface)方法)时,编码规则不同。

对于 P/InvokeCharSet可以显式地与任何 [DllImport] 属性一起使用,或通过 [module|assembly: DefaultCharSet(CharSet.Auto|Ansi|Unicode)] 隐式地使用,以更改默认设置对于每个模块或每个程序集的所有 [DllImport] 声明。

默认值是CharSet.Ansi,这意味着会有Unicode到ANSI的转换。我通常使用 [module: DefaultCharSet(CharSet.Unicode)] 将默认值更改为 Unicode,然后在那些罕见的情况下选择性地使用 [DllImport(CharSet = CharSet.Ansi)]我需要调用 ANSI API 的情况。

还可以使用 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 将与单字节数组相互转换字节值。请注意,对于 buff2,这仍然是一种智能的 Unicode 到 OS 当前代码页(和返回)转换。例如,Unicode '\x20AC' () 在非托管代码中将变为 \x80(假设操作系统代码页为 Windows-1252 ).这就是 MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1)] char[] buffMarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1)] ushort 的不同之处[] 浅黄色。对于 ushort0x20AC 将简单地转换为 0xAC

对于调用 COM 接口(interface)方法,情况就完全不同了。在那里,char 始终被视为表示 Unicode 字符的双字节值。也许,这种设计决策的原因可以从 Don Box 的“Essential COM”中得到暗示(引用来自 this page 的脚注):

The OLECHAR type was chosen in favor of the common TCHAR data type used by the Win32 API to alleviate the need to support two versions of each interface (CHAR and WCHAR). By supporting only one character type, object developers are decoupled from the state of the UNICODE preprocessor symbol used by their clients.

显然,同样的概念也进入了 .NET。我非常有信心即使对于遗留的 ANSI 平台也是如此(例如 Windows 95,其中 Marshal.SystemDefaultCharSize == 1)。

请注意,当 DefaultCharSet 是 COM 接口(interface)方法签名的一部分时,它对 char 没有任何影响。也没有办法显式应用 CharSet。但是,您仍然可以使用 MarshalAs 完全控制每个单独参数的编码行为,其方式与上面的 P/Invoke 完全相同。例如,您的 Next 方法可能如下所示,以防非托管 COM 代码需要一个 ANSI 字符缓冲区:

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

关于c# - COM 方法、Char 类型和 CharSet,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25039290/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com