从 Marshal.PtrToStringAnsi 返回的字符串的编码是什么?

What is the encoding of the string returned from Marshal.PtrToStringAnsi?

我正在实施自定义封送拆收器,以发送 utf8 字符串 from/to 本机 from/to 托管。

{
    [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 Doc,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 函数的字符串编码是什么?

是否有更简单的方法来验证字符串是否来自该函数?

what is the Encoding of the string from Marshal.PtrToStringAnsi function?

没有"ANSI"编码。它是您系统的当前代码页。这将取决于用户的区域设置。这应该对应于 CharSet enum:

Ansi: Marshal strings as multiple-byte character strings: the system default Windows (ANSI) code page on Windows, and UTF-8 on Unix.

注意 Unix 上的特殊处理(我想,Linux)。

Is there a simpler way to verify if the string is from that function?

在我看来,这似乎是一个与主要问题完全不同的问题。特别是:在我看来,知道从 "ANSI" 转换为 UTF-16(.NET 使用的内部文本编码)时函数将使用什么编码并不会导致 "verify if the string is from that function"。一旦你有了 C# string 对象,它就已经被编码为 UTF-16。它实际上可能源自 any 编码。

从你的问题中也不清楚你所说的 "works except when the C# string is from Marshal.PtrToStringAnsi function" 是什么意思。也就是说,精确地在那种情况下它以什么方式不起作用?您的封送拆收器似乎只负责将 UTF-8 字节传入或传出本机代码。给定一个 C# string 对象,该 string 的创建方式应该无关紧要。它现在是一串 UTF-16 字符,可以可靠地重新编码为 UTF-8。如果 "ANSI" 文本有问题,则该问题在您的封送拆收器介入之前就已发生。您的封送拆收器不必为此担心。

最后:为什么不直接使用 Encoding.UTF8 而不是在每个封送处理操作中实例化一个新的 UTF8Encoding 对象?至少,您应该缓存该对象,但是由于 GetBytes()GetString() 对于 UTF8Encoding 的任何实例都一样工作,实际上您应该只使用 .NET 已有的对象为您创建,并让 .NET 处理缓存对象。