Как я могу вызвать многобайтовую строку ANSI? - PullRequest
4 голосов
/ 30 апреля 2009

Я работаю над оболочкой 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.</p> <p>How would I tell PInvoke to marshal the string as a multibyte string? I see there's a <code>WideCharToMultiByte() API-функция. Могу ли я изменить сигнатуру, чтобы ожидать IntPtr на строку, которую я создаю в неуправляемой памяти? Кажется, что это все еще имеет много проблем, с которыми сталкивается текущая реализация (возможно, придется отбрасывать или заменять символы), поэтому я не уверен, является ли это улучшением. Есть ли другой метод маршалинга, который мне не хватает?

Ответы [ 3 ]

6 голосов
/ 30 апреля 2009

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

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

1 голос
/ 30 апреля 2009

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

0 голосов
/ 30 апреля 2009

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

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

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