DllImport - ANSI 与 Unicode

Posted

技术标签:

【中文标题】DllImport - ANSI 与 Unicode【英文标题】:DllImport - ANSI vs. Unicode 【发布时间】:2013-07-23 10:47:07 【问题描述】:

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

问题:您编写以下代码段以使用平台调用从 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 不起作用?

【问题讨论】:

【参考方案1】:
 string personName = "N?el";

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

 string personName = "Nöel";

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

MessageBoxA 是旧版 Windows 中使用的遗留函数,早在过去,程序仍使用 8 位字符串。它并没有完全消失,许多 C 和 C++ 程序仍然倾向于使用 8 位编码。 MessageBoxA 是implemented by converting the 8-bit encoded strings to Unicode,然后调用 MessageBoxW。如果你一开始就有一个 Unicode 字符串,这会很慢而且有损。

因此对 4 个版本进行评分:

A:使用 MessageBoxA + 8 位编码,有风险。 B:使用 MessageBoxA + Unicode,失败。 C:使用 MessageBoxW + Unicode,很好。 D:使用 MessageBoxA + Unicode,失败。

【讨论】:

【参考方案2】:

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 调用代码中存在的完全相同的文本。

【讨论】:

【参考方案3】:

我怀疑答案在personName

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

string personName = "N?el";

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

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

【讨论】:

以上是关于DllImport - ANSI 与 Unicode的主要内容,如果未能解决你的问题,请参考以下文章

C# 中用于从 DllImport 函数中检索引用的指针

使用demjson解析unicod

将 MediaInfo DLL 与 C# DLLImport 一起使用

dllimport与dllexport作用与区别

编组字符 *

用ofstream/ifstream 读写Unicod的TXT