关于 c#:DllImport – ANSI 与 Unicode

DllImport - ANSI vs. Unicode

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 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";

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

1
 string personName ="N??el";

??往往是一个问题,它的字符代码不在 ASCII 字符集中,并且可能不受默认系统代码页的支持。当您调用 ANSI 版本的 MessageBox(又名 MessageBoxA)时使用的是什么。真正的函数是 MessageBoxW,它采用 utf-16 编码的 Unicode 字符串。

MessageBoxA 是旧版本的 Windows 中使用的遗留函数,早在过去,程序仍然使用 8 位字符串。它并没有完全消失,许多 C 和 C 程序仍然倾向于使用 8 位编码。 MessagBoxA 是通过将 8 位编码字符串转换为 Unicode,然后调用 MessageBoxW 来实现的。如果你一开始就有一个 Unicode 字符串,那么 With 会很慢而且有损。

所以给4个版本打分:

A:使用 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 编组器使用 CharSet 参数的值选择适当的入口点。

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


我怀疑答案在 personName.

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

1
string personName ="N?el";

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

无论如何,Unicode 可以处理比 ANSI 更多的格式,所以它是一个更好的默认选择。