我正在尝试使用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传递其他数据结构,以使其成功返回区域性名称?]
症状:GetUserDefaultLocaleName()返回值> 0
(成功),但是StringBuilder缓冲区似乎仅包含1个字符。
Charset.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.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
应该被导入,而不是硬编码为LOCALE_NAME_MAX_LENGTH
,但这就是您可以在C#中执行的操作:)