DllImport - ANSI与Uni​​code

时间:2013-07-23 10:47:07

标签: c# pinvoke

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

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

3 个答案:

答案 0 :(得分:10)

 string personName = "N?el";

这个字符串因此问题所引发的确切问题而出现乱码。毫无疑问,它原来看起来像这样:

 string personName = "Nöel";

ö往往是个问题,它的字符代码不是ASCII字符集,默认系统代码页不支持 。这是当你pinvoke ANSI版本的MessageBox,也就是MessageBoxA时使用的。真正的函数是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,失败。

答案 1 :(得分: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调用代码中存在的完全相同的文本。

答案 2 :(得分:0)

我怀疑答案在personName

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

string personName = "N?el";

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

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