P/Invoke 未使用调用 GetUserDefaultLocaleName() 的 StringBuilder 参数返回预期结果
P/Invoke not returning expected result with StringBuilder parameter for call to GetUserDefaultLocaleName()
我正在尝试使用 GetUserDefaultLocaleName 获取当前在区域设置控制面板中设置的区域设置的区域性名称。但是,当我将 StringBuilder 作为参数传递时,我没有得到预期的结果。
static class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern int GetUserDefaultLocaleName(StringBuilder buf, int bufferLength);
}
static void Main(string[] args)
{
const int nChars = 256;
var sb = new StringBuilder(nChars);
int cultureNameLength = NativeMethods.GetUserDefaultLocaleName(sb, nChars);
string cultureName = sb.ToString();
Console.WriteLine(cultureName);
Console.ReadLine();
}
如果我将当前文化设置为 "en-US",当我 运行 代码时,cultureName 设置为 "e"(仅当前文化的第一个字母),但 cultureNameLength设置为 6(预期为空终止字符串 "en-US[=21=]")
为什么这只是 return 文化名称的第一个字母(我也测试了其他文化和 "fr-FR" returns "f")?有没有我可以传递的不同的数据结构而不是 StringBuilder 来成功地获得它 return 区域性名称?
症状:GetUserDefaultLocaleName() returns 一个值 > 0
(成功),但 StringBuilder 缓冲区似乎只包含 1 个字符。
- DllImport attribute's Charset 字段默认值设置为
Charset.Ansi
,因此平台调用编组字符串从其托管格式 (Unicode) 到 ANSI 格式。
- 由于字符串是以 Unicode 格式生成的,因此编组的 ANSI 字符串在第一个空 (
[=13=]
) 字符处被截断。
Use this field with a member of the CharSet enumeration to specify the marshaling behavior of string parameters and to specify which
entry-point name to invoke (the exact name given or a name ending with
"A" or "W"). The default enumeration member for C# and Visual Basic is
CharSet.Ansi and the default enumeration member for C++ is
CharSet.None, which is equivalent to CharSet.Ansi. In Visual Basic,
you use the Declare statement to specify the CharSet field.
另请参阅:Specifying a Character Set
要解决问题,请明确设置CharSet = CharSet.Unicode
。
CharSet = CharSet.Auto
也可以解决问题,但最好不要相信它,字符串是以 Unicode 格式生成的。最好不要让目标平台确定不同的字符串格式,它可能会失败。
internal const int LOCALE_NAME_MAX_LENGTH = 85;
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern int GetUserDefaultLocaleName(StringBuilder buf, int bufferLength);
// [...]
var sb = new StringBuilder(LOCALE_NAME_MAX_LENGTH);
int locale = GetUserDefaultLocaleName(sb, LOCALE_NAME_MAX_LENGTH);
注意:LOCALE_NAME_MAX_LENGTH
应该被导入,而不是硬编码为 85
,但这是您可以在 C# 中执行的操作:)
我正在尝试使用 GetUserDefaultLocaleName 获取当前在区域设置控制面板中设置的区域设置的区域性名称。但是,当我将 StringBuilder 作为参数传递时,我没有得到预期的结果。
static class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern int GetUserDefaultLocaleName(StringBuilder buf, int bufferLength);
}
static void Main(string[] args)
{
const int nChars = 256;
var sb = new StringBuilder(nChars);
int cultureNameLength = NativeMethods.GetUserDefaultLocaleName(sb, nChars);
string cultureName = sb.ToString();
Console.WriteLine(cultureName);
Console.ReadLine();
}
如果我将当前文化设置为 "en-US",当我 运行 代码时,cultureName 设置为 "e"(仅当前文化的第一个字母),但 cultureNameLength设置为 6(预期为空终止字符串 "en-US[=21=]")
为什么这只是 return 文化名称的第一个字母(我也测试了其他文化和 "fr-FR" returns "f")?有没有我可以传递的不同的数据结构而不是 StringBuilder 来成功地获得它 return 区域性名称?
症状:GetUserDefaultLocaleName() returns 一个值 > 0
(成功),但 StringBuilder 缓冲区似乎只包含 1 个字符。
- DllImport attribute's Charset 字段默认值设置为
Charset.Ansi
,因此平台调用编组字符串从其托管格式 (Unicode) 到 ANSI 格式。 - 由于字符串是以 Unicode 格式生成的,因此编组的 ANSI 字符串在第一个空 (
[=13=]
) 字符处被截断。
Use this field with a member of the CharSet enumeration to specify the marshaling behavior of string parameters and to specify which entry-point name to invoke (the exact name given or a name ending with "A" or "W"). The default enumeration member for C# and Visual Basic is CharSet.Ansi and the default enumeration member for C++ is CharSet.None, which is equivalent to CharSet.Ansi. In Visual Basic, you use the Declare statement to specify the CharSet field.
另请参阅:Specifying a Character Set
要解决问题,请明确设置CharSet = CharSet.Unicode
。
CharSet = CharSet.Auto
也可以解决问题,但最好不要相信它,字符串是以 Unicode 格式生成的。最好不要让目标平台确定不同的字符串格式,它可能会失败。
internal const int LOCALE_NAME_MAX_LENGTH = 85;
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern int GetUserDefaultLocaleName(StringBuilder buf, int bufferLength);
// [...]
var sb = new StringBuilder(LOCALE_NAME_MAX_LENGTH);
int locale = GetUserDefaultLocaleName(sb, LOCALE_NAME_MAX_LENGTH);
注意:LOCALE_NAME_MAX_LENGTH
应该被导入,而不是硬编码为 85
,但这是您可以在 C# 中执行的操作:)