I am trying to get the culture name for the locale that is currently set in the regional settings control panel using GetUserDefaultLocaleName. However, I am not getting the expected results when I pass a StringBuilder as the parameter.
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();
}
If I have my current culture set to "en-US", when I run the code, cultureName gets set to "e" (only the first letter of current culture), but cultureNameLength is set to 6 (which is expected for the null terminated string "en-US\0")
Why is this only returning the first letter of the culture name (I also tested other cultures and "fr-FR" returns "f")? Is there a different data structure that I can pass instead of StringBuilder to get this to successfully return the culture name?
Symptoms: GetUserDefaultLocaleName() returns a value > 0
(successful), but the StringBuilder buffer appears to only contain 1 char.
Charset.Ansi
, thus platform invoke marshals strings from their managed format (Unicode) to ANSI format. \0
) char. 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.
See also: Specifying a Character Set
To solve the problem, set explicitly CharSet = CharSet.Unicode
.
CharSet = CharSet.Auto
would also solve the problem, but better not trust it, the string is generated in Unicode format. Better not let the target platform determine a different string format, it may fail.
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);
Note: LOCALE_NAME_MAX_LENGTH
should be imported, not hard-coded to 85
, but that's what you can do in C# :)