P /使用StringBuilder参数调用GetUserDefaultLocaleName()不会返回预期结果

问题描述 投票:1回答:1

我正在尝试使用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 \ 0”)

为什么只返回文化名称的首字母(我也测试了其他文化,而“ fr-FR”返回“ f”)?是否可以代替StringBuilder传递其他数据结构,以使其成功返回区域性名称?]

c# .net pinvoke
1个回答
2
投票

症状:GetUserDefaultLocaleName()返回值> 0(成功),但是StringBuilder缓冲区似乎仅包含1个字符。

  • DllImport属性的Charset字段默认值设置为Charset.Ansi,因此平台调用将字符串从其托管格式(Unicode)转换为ANSI格式。
  • 由于该字符串以Unicode格式生成,因此经过封送处理的ANSI字符串在第一个空(\0)字符处被截断。

将此字段与CharSet枚举的成员一起使用以指定字符串参数的封送处理行为,并指定要调用的入口点名称(给定的确切名称或以结尾的名称“ A”或“ W”)。 C#和Visual Basic的默认枚举成员是CharSet.Ansi和C ++的默认枚举成员是CharSet.None,等效于CharSet.Ansi。在Visual Basic中,您可以使用Declare语句指定CharSet字段。

另请参见:Specifying a Character Set

要解决该问题,请显式设置CharSet = CharSet.UnicodeCharSet = 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应该被导入,而不是硬编码为LOCALE_NAME_MAX_LENGTH,但这就是您可以在C#中执行的操作:)

© www.soinside.com 2019 - 2024. All rights reserved.