P / Invoke не возвращает ожидаемый результат с параметром StringBuilder для вызова GetUserDefaultLocaleName () - PullRequest
1 голос
/ 04 мая 2020

Я пытаюсь получить название культуры для локали, которая в данный момент установлена ​​в панели управления региональными настройками, используя 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, чтобы она успешно возвращала имя культуры?

1 Ответ

2 голосов
/ 04 мая 2020

Симптомы: GetUserDefaultLocaleName () возвращает значение > 0 (успешно), но буфер StringBuilder содержит только 1 символ.

  • Поле атрибута Charset атрибута DllImport по умолчанию установлено на Charset.Ansi, таким образом, платформа вызывает строки маршалов из их управляемого формата (Юникод) в формате ANSI.
  • Поскольку строка генерируется в формате Unicode, маршалированная строка ANSI усекается до первого нулевого (\0) символа.

Используйте это поле с членом перечисления CharSet, чтобы указать поведение маршалинга строковых параметров и указать, какое имя точки входа вызывать (точное имя или имя, оканчивающееся на «A» или «W»). Элементом перечисления по умолчанию для C# и Visual Basi c является CharSet.Ansi, а элементом перечисления по умолчанию для C ++ является CharSet.None, что эквивалентно CharSet.Ansi. В Visual Basi c вы используете оператор Declare для указания поля CharSet.

См. Также: Указание набора символов

Кому решить проблему, установить явно 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# :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...