Как мне вызвать многобайтовую строку ANSI?

StackOverflow https://stackoverflow.com/questions/808154

  •  03-07-2019
  •  | 
  •  

Вопрос

Я работаю над оболочкой PInvoke для библиотеки, которая не поддерживает строки Unicode, но поддерживает многобайтовые строки ANSI.Изучая отчеты FxCop о библиотеке, я заметил, что используемая маршалинг строк имеет некоторые интересные побочные эффекты.Метод PInvoke использовал сопоставление «наилучшего соответствия» для создания однобайтовой строки ANSI.Для иллюстрации вот как выглядел один метод:

[DllImport("thedll.dll", CharSet=CharSet.Ansi)]
public static extern int CreateNewResource(string resourceName);

Результатом вызова этой функции со строкой, содержащей символы, отличные от ASCII, является то, что Windows находит «закрытый» символ, обычно это выглядит так, как будто это «???».Если мы представим, что «a» не является символом ASCII, то передача «cat» в качестве параметра создаст ресурс с именем «c?t».

Если я буду следовать рекомендациям правила FxCop, у меня получится что-то вроде этого:

[DllImport("thedll.dll", CharSet=CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
public static extern int CreateNewResource([MarshalAs(UnmanagedType.LPStr)] string resourceName);

Это приводит к изменению поведения;теперь, когда персонаж не может быть сопоставлен, выдается исключение.Это меня беспокоит, потому что это критическое изменение, поэтому я хотел бы попытаться маршалировать строки как многобайтовые ANSI, но не вижу способа сделать это. UnmanagedType.LPStr указывается как однобайтовая строка ANSI, LPTStr will be Unicode or ANSI depending on the system, and LPWStr is not what the library expects.

How would I tell PInvoke to marshal the string as a multibyte string? I see there's a WideCharToMultiByte() API function, could I change the signature to expect an IntPtr в строку, которую я создаю в неуправляемой памяти?Похоже, что здесь все еще есть многие проблемы, которые есть в текущей реализации (возможно, придется отбросить или заменить символы), поэтому я не уверен, является ли это улучшением.Есть ли другой метод маршалинга, который мне не хватает?

Это было полезно?

Решение

АНСИ является многобайтовые, а строки ANSI кодируются в соответствии с кодовой страницей, включенной в данный момент в системе. WideCharToMultiByte работает так же, как P/Invoke.

Возможно, вам нужно конвертировать в UTF-8.Хотя WideCharToMultiByte поддерживает это, я не думаю, что P/Invoke поддерживает, поскольку невозможно принять UTF-8 в качестве общесистемной кодовой страницы ANSI.На этом этапе вы будете рассматривать передачу строки как IntPtr вместо этого, хотя если вы это делаете, вы также можете использовать управляемый Encoding класс для преобразования, а не WideCharToMultiByte.

Другие советы

Вот лучший способ, который я нашел для этого.Вместо маршалинга в виде строки маршалируйте как байт[].Возложите ответственность на вызывающую сторону API-функции pinvoke за преобразование в массив байтов наиболее подходящим способом.Скорее всего, с помощью одного из классов Text.Encoding.

Если вам в конечном итоге придется вызывать WideCharToMultiByte вручную, я бы избавился от p/invoke и вручную упорядочил его, используя WideCharToMultiByte в функции-оболочке C++/CLI.Управляемый C++ гораздо лучше справляется с этими сценариями взаимодействия, чем C#.

Хотя, если это единственный p/invoke, который у вас есть, вероятно, оно того не стоит.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top