我今天正在玩Visual Studio,用一些C#来从Win32 API中获取一些东西。那是我注意到这个标题(请在新标签中打开以查看完整尺寸)
它的内容如下:
虽然公共语言运行库默认值为System.Runtime.InteropServices.CharSet.Auto,但语言可能会覆盖此默认值。例如,默认情况下,C#将所有方法和类型标记为System.Runtime.InteropServices.CharSet.Ansi。
为何选择ANSI?从阅读Mark Russinovich的 Windows Internals,我读过:
因为许多应用程序处理8位(单字节)ANSI字符串,许多Windows 接受字符串参数的函数有两个入口点:Unicode(宽,16位)版本和 ANSI(窄,8位)版本。 如果您调用Windows函数的窄版本,则会在处理之前将输入字符串参数转换为Unicode,从而对性能产生轻微影响 由系统和输出参数在返回之前从Unicode转换为ANSI 申请。
那么,我是否正确理解PInvoking非托管代码时C#的默认值是否接受性能影响?
修改
所以,如果我这样做:
[DllImport("kernel32.dll", Charset = CharSet.Auto]
public static extern bool Foo(IntPtr hHandle);
让我们说在kernel32.dll中,存在FooA
和FooW
... C#如何知道要使用哪个入口点? Visual Studio中的帮助文本让我认为它默认会选择ANSI入口点,但如果可以避免性能影响(但可忽略不计),我们更喜欢宽版本。
答案 0 :(得分:4)
Pinvoke不仅用于调用winapi功能。实际上它使用较少,因为.NET Framework已经包含了大量的winapi。更常见的是它用于调用遗留的自定义C代码。从本网站关于pinvoke的大多数问题中可以清楚地看到。 Charset.Ansi的默认值只是匹配C语言中的默认字符类型, char 是一个8位类型。
是的,如果你使用pinvoke来调用一个unwrap的winapi函数,那么使用CharSet.Auto对于避免数据损坏和转换开销非常重要。 pinvoke marshaller完全不知道它是一个winapi函数,包含这些函数的Windows DLL与自定义DLL无法区分。请注意,Auto本身已经不再具有相关性了,您的代码在今天启动Windows 98或ME的计算机上运行的几率非常小。
请注意,您的pinvoke声明不是很有意义,而且通常是不明智的。只有带有字符串参数的winapi函数或指向包含字符串的结构的指针才需要CharSet属性。而且你几乎总是将参数声明为String,StringBuffer或struct类型,以允许pinvoke marshaller将其正确。如果您使用IntPtr然后负担是生成正确的字符串,您将必须显式使用Marshal.StringToHGlobalAnsi / Auto / Uni。或Marshal.StructureToPtr(),如果它是一个结构,也有一个适当的[StructLayout],它也有一个CharSet属性。
pinvoke marshaller具有winapi功能的内置知识,在其名称后有额外的A或W.它只是尝试首先找到没有额外字母的函数,然后尝试A或W版本。 EntryPoint属性可用于禁用该探测。这只发生过一次,所以使用它没什么意义。