DllImport – ANSI与Uni​​code

我对下面的测试问题的可能答案有一些疑问:

问题:您编写以下代码段以使用平台调用从Win32应用程序编程接口(API)调用函数。

string personName = "N?el"; string msg = "Welcome" + personName + "to club"!"; bool rc = User32API.MessageBox(0, msg, personName, 0); 

您需要定义一个最能编组字符串数据的方法原型。 你应该使用哪个代码段?

 // A. [DllImport("user32", CharSet = CharSet.Ansi)] public static extern bool MessageBox(int hWnd, string text, string caption, uint type); } // B. [DllImport("user32", EntryPoint = "MessageBoxA", CharSet = CharSet.Ansi)] public static extern bool MessageBox(int hWnd, [MarshalAs(UnmanagedType.LPWStr)]string text, [MarshalAs(UnmanagedType.LPWStr)]string caption, uint type); } // C. - Correct answer [DllImport("user32", CharSet = CharSet.Unicode)] public static extern bool MessageBox(int hWnd, string text, string caption, uint type); } // D. [DllImport("user32", EntryPoint = "MessageBoxA", CharSet = CharSet.Unicode)] public static extern bool MessageBox(int hWnd, [MarshalAs(UnmanagedType.LPWStr)]string text, [MarshalAs(UnmanagedType.LPWStr)]string caption, uint type); } 

为什么正确答案是C? 难道它也不是A? 唯一的区别是它将是ANSI而不是Unicode。

我知道它不能是D,因为我们选择Unicode作为字符集,然后使用ANSI函数作为入口点。

B为什么不工作?

  string personName = "N?el"; 

这个字符串被这个问题所询问的确切问题弄乱了。 毫无疑问,它在原文中看起来像这样:

  string personName = "Nöel"; 

ö往往是一个问题,它的字符代码不是ASCII字符集,默认系统代码页可能不支持。 当您对MessageBox的ANSI版本(即MessageBoxA)进行pinvoke时使用的是什么。 真正的函数是MessageBoxW,它采用utf-16编码的Unicode字符串。

MessageBoxA是旧版Windows中使用的遗留函数,早在程序仍然使用8位字符串时。 它并没有完全消失,许多C和C ++程序仍然倾向于使用8位编码。 MessagBoxA通过将8位编码字符串转换为Unicode然后调用MessageBoxW来实现。 如果您首先使用Unicode字符串,那么速度慢且有损。

所以评价4个版本:

答:使用MessageBoxA + 8位编码,有风险。
B:使用MessageBoxA + Unicode,失败。
C:使用MessageBoxW + Unicode,不错
D:使用MessageBoxA + Unicode,失败。

除非另有说明,否则CharSet.Ansi告诉编组人员将编组为ANSI。 同样,除非另有说明,否则CharSet.Unicode是以UTF-16编组的指令。

由于选项B和D确实以其他方式指示,因此会覆盖CharSet参数,因此选项B和D实际上是等效的。 它们都是不正确的,因为你要求名为MessageBoxA的函数需要ANSI文本。

这留下了A和C.选项A调用函数MessageBoxA的ANSI变体,选项C调用Unicode变体MessageBoxW 。 在幕后,p / invoke marshaller使用CharSet参数的值选择适当的入口点。

现在,您可以使用A或C,但区别仅在于选项A,您将传递ANSI编码文本。 如果您传递的文本包含无法用ANSI编码的字符,则会丢失信息。 这就是C首选的原因。 它将始终接收.net调用代码中存在的完全相同的文本。

我怀疑答案是在personName

我认为它没有正确地复制粘贴到您的问题中。

 string personName = "N?el"; 

注意? 字符。 我认为这表明原始字符串在那里有一个非ANSI字符。 如果这是真的,你可以正确地看到它,那么它表明你必须使用Unicode而不是ANSI(因此答案必须是C )。

在任何情况下,Unicode都可以使用比ANSI更多的格式,因此它是更好的默认选择。