Какова кодировка строки, возвращаемой из Marshal.PtrToStringAnsi? - PullRequest
1 голос
/ 15 октября 2019

Я реализую пользовательский Marshaler для отправки строки utf8 из / в native из / в managed.

{
    [ComVisible(true)]
    public class UTF8StringMarshaler : ICustomMarshaler
    {
        private static ICustomMarshaler marshalerInstance = new UTF8StringMarshaler();

        public static ICustomMarshaler GetInstance(string optionalCookie)
        {
            return marshalerInstance;
        }
        public void CleanUpManagedData(object ManagedObj)
        {
            //Managed Data will be deleted by the garbage collector
        }

        public void CleanUpNativeData(IntPtr pNativeData)
        {
            Marshal.FreeCoTaskMem(pNativeData);
        }

        public int GetNativeDataSize()
        {
            //Not used in our case
            return -1;
        }

        public IntPtr MarshalManagedToNative(object ManagedObj)
        {
            if (ManagedObj == null || ManagedObj as string == null)
                return IntPtr.Zero;

            if (!(ManagedObj is string))
                throw new MarshalDirectiveException("UTF8StringMarshaler can only be used on String.");

            UTF8Encoding utf8Encoder = new UTF8Encoding();
            string utf8string = ManagedObj as string;
            byte[] stringBuffer = utf8Encoder.GetBytes(utf8string);
            IntPtr buffer = Marshal.AllocCoTaskMem(stringBuffer.Length + 1);
            Marshal.Copy(stringBuffer, 0, buffer, stringBuffer.Length);
            Marshal.WriteByte(buffer + stringBuffer.Length, 0);
            return buffer;
        }

        public unsafe object MarshalNativeToManaged(IntPtr pNativeData)
        {
            if (pNativeData == IntPtr.Zero)
                return null;
            string temp = null;
            UTF8Encoding utf8Encoder = new UTF8Encoding(true, true);
            byte* buffer = (byte*)pNativeData;
            while (*buffer != 0)
            {
                buffer++;
            }
            int length = (int)(buffer - (byte*)pNativeData);

            byte[] stringbuffer = new byte[length];

            Marshal.Copy(pNativeData, stringbuffer, 0, length);
            try
            {
                temp = utf8Encoder.GetString(stringbuffer);
            }
            catch (EncoderFallbackException e)
            {
                Console.WriteLine("Encoding Exception type {0}, Error {1}", e.GetType().Name, e.Message);
            }
            return temp;
        }
    }

Эта реализация работает, кроме случаев, когда строка C # является функцией Marshal.PtrToStringAnsi.

, поэтому в функции MarshalNativeToManaged мне нужно проверить, является ли строка правильной кодировкой из Marshal.PtrToStringAnsi

Из документа Microsoft, Marshal.PtrToStringAnsi расширяет каждый символ ANSI до Unicode:

Copies all characters up to the first null character from an unmanaged ANSI string to a managed String, and widens each ANSI character to Unicode.

Итак, вопрос в том, какова кодировка строки из функции Marshal.PtrToStringAnsi?

Есть ли более простой способ проверить, является ли строка изэта функция?

1 Ответ

1 голос
/ 15 октября 2019

что такое кодировка строки из функции Marshal.PtrToStringAnsi?

Нет единой кодировки "ANSI". Это текущая кодовая страница вашей системы. Это будет зависеть от настроек локали пользователя. Это должно соответствовать CharSet enum :

Ansi : маршальные строки в виде многобайтовых символьных строк: системный код Windows (ANSI) по умолчаниюстраница в Windows и UTF-8 в Unix.

Обратите внимание на специальную обработку в Unix (и, я полагаю, в Linux).

Есть ли прощеспособ проверить, является ли строка из этой функции?

Мне кажется, что это совершенно другой вопрос, нежели основной. В частности: знание того, какую кодировку будет использовать функция при преобразовании из «ANSI» в UTF-16 (внутренняя кодировка текста, используемая в .NET), не дает мне способа «проверить, является ли строкаиз этой функции ". Если у вас есть объект C # string, он уже закодирован как UTF-16. Это могло произойти из практически любой кодировки.

Из вашего вопроса также неясно, что вы подразумеваете под "работает, за исключением случаев, когда строка C # из Marshal.PtrToStringAnsi function" . То есть каким образом точно не работает в этом сценарии? Похоже, ваш маршалер отвечает только за передачу байтов UTF-8 в нативный код или из него. Учитывая объект C # string, никогда не должно иметь значения, как был создан string. Теперь это строка символов UTF-16, которую можно надежно перекодировать как UTF-8. Если есть проблема с текстом «ANSI», эта проблема возникла до того, как ваш маршалер вмешался. Ваш маршалер не должен беспокоиться об этом.

Наконец: почему бы просто не использовать Encoding.UTF8 вместо создания нового объекта UTF8Encoding при каждой операции маршалинга? По крайней мере, вы должны кэшировать объект, но так как GetBytes() и GetString() работают одинаково для любого экземпляра UTF8Encoding, на самом деле вы должны просто использовать тот, который .NET уже создал для вас, и позволить.NET занимается кэшированием объекта.

...