MarshalAs(UnmanagedType.LPStr) - 如何将utf-8字符串转换为char *

时间:2012-11-08 12:30:03

标签: c# c++ marshalling

问题标题基本上是我想问的问题:

[MarshalAs(UnmanagedType.LPStr)] - 如何将utf-8字符串转换为char *?

当我尝试在c#和c ++ dll之间进行通信时,我使用上面的行; 更具体地说,介于:

之间
somefunction(char *string) [c++ dll]

somefunction([MarshalAs(UnmanagedType.LPStr) string text) [c#]

当我通过c#发送我的utf-8文本(scintilla.Text)并进入我的c ++ dll时, 我在VS 10调试器中显示:

  1. c#字符串已成功转换为char*

  2. 结果char*在观察窗口中正确反映了相应的utf-8字符(包括韩文字母)。

  3. 这是截图(包含更多细节):

    ss

    如您所见,initialScriptText[0]返回单byte(char):'B',并且char* initialScriptText的内容在VS观看窗口中正确显示(包括韩语)。

    通过char指针,似乎每个byte将英语保存为char,而韩语似乎每char个保存为两个字节。 (截图中的韩文单词是3个字母,因此以6个字节保存)

    这似乎表明每个“字母”不会保存在相同大小的容器中,但会因语言而异。 (可能暗示类型?)

    我正在尝试在纯c ++中实现相同的结果:读取utf-8文件并将结果保存为char*

    以下是我尝试读取utf-8文件并在c ++中转换为char*的示例:

    ss2

    意见:

    1. wchar_t*转换为char*
    2. 时视觉丢失
    3. 从结果开始,s8正确显示字符串,我知道我已成功将wchar_t*中的utf-8文件内容转换为char*
    4. 因为'result'保留了我直接从文件中获取的字节,但是我得到的结果与我通过c#获得的结果不同(我使用了相同的文件),我得出的结论是c# marshal已将文件内容放入其他一些程序,以进一步将文本变更为char*
    5. (截图还显示了我使用wcstombs的可怕失败)

      注意:我正在使用(http://utfcpp.sourceforge.net/

      中的utf8标头

      如果我的代码/观察中有任何错误,请纠正我。

      我希望能够模仿我通过c#marshal获得的结果,并且在经历了所有这些之后我意识到我完全陷入困境。有什么想法吗?

4 个答案:

答案 0 :(得分:12)

  

[MarshalAs(UnmanagedType.LPStr)] - 如何将utf-8字符串转换为char *?

没有。在托管代码中没有“utf-8字符串”这样的东西,字符串总是以utf-16编码。使用默认系统代码页完成从LPStr到LPStr的编组。除非您使用代码页949,否则在调试器中看到韩语字形会非常值得注意。

如果与utf-8互操作是一项硬性要求,则需要在pinvoke声明中使用byte []。并使用System.Text.Encoding.UTF8自行转换。使用其GetString()方法将byte []转换为字符串,使用其GetBytes()方法将字符串转换为byte []。如果可能的话,通过在本机代码中使用wchar_t []来避免所有这些。

答案 1 :(得分:5)

虽然其他答案是正确的,但.NET 4.7已经有了重大进展。现在有一个选项可以完全符合UTF-8的要求:UnmanagedType.LPUTF8Str。我尝试了它,它就像一个瑞士计时器,完全听起来像它。

事实上,我甚至在一个参数中使用了MarshalAs(UnmanagedType.LPUTF8Str)而在另一个参数中使用了MarshalAs(UnmanagedType.LPStr)。也有效。这是我的方法(接受字符串参数并通过参数返回一个字符串):

[DllImport("mylib.dll", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern void ProcessContent([MarshalAs(UnmanagedType.LPUTF8Str)]string content, [MarshalAs(UnmanagedType.LPUTF8Str), Out]StringBuilder outputBuffer,[MarshalAs(UnmanagedType.LPStr)]string settings);

谢谢,微软!另一个麻烦消失了。

答案 2 :(得分:3)

如果您需要编组UTF-8 string,请手动执行此操作。

使用IntPtr而不是字符串定义函数:

somefunction(IntPtr text)

然后将文本转换为以零结尾的UTF8字节数组并将其写入IntPtr

byte[] retArray = Encoding.UTF8.GetBytes(text);
byte[] retArrayZ = new byte[retArray.Length + 1];
Array.Copy(retArray, retArrayZ, retArray.Length);
IntPtr retPtr = AllocHGlobal(retArrayZ.Length);
Marshal.Copy(retArrayZ, 0, retPtr, retArrayZ.Length);
somefunction(retPtr);      

答案 3 :(得分:0)

如果使用早于4.7的.NET Framework,可以使用

ICustomMarshaler


        class UTF8StringCodec : ICustomMarshaler
        {
            public static ICustomMarshaler GetInstance(string cookie) => new UTF8StringCodec();

            public void CleanUpManagedData(object ManagedObj)
            {
                // nop
            }

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

            public int GetNativeDataSize()
            {
                throw new NotImplementedException();
            }

            public IntPtr MarshalManagedToNative(object ManagedObj)
            {
                var text = $"{ManagedObj}";
                var bytes = Encoding.UTF8.GetBytes(text);
                var ptr = Marshal.AllocCoTaskMem(bytes.Length + 1);
                Marshal.Copy(bytes, 0, ptr, bytes.Length);
                Marshal.WriteByte(ptr, bytes.Length, 0);
                return ptr;
            }

            public object MarshalNativeToManaged(IntPtr pNativeData)
            {
                if (pNativeData == IntPtr.Zero)
                {
                    return null;
                }

                var bytes = new MemoryStream();
                var ofs = 0;
                while (true)
                {
                    var byt = Marshal.ReadByte(pNativeData, ofs);
                    if (byt == 0)
                    {
                        break;
                    }
                    bytes.WriteByte(byt);
                    ofs++;
                }

                return Encoding.UTF8.GetString(bytes.ToArray());
            }
        }

P /调用声明:

        [DllImport("native.dll", CallingConvention = CallingConvention.Cdecl)]
        private extern static int NativeFunc(
            [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8StringCodec))] string path
        );

回调内的用法:

        [StructLayout(LayoutKind.Sequential)]
        struct Options
        {
            [MarshalAs(UnmanagedType.FunctionPtr)]
            public CallbackFunc callback;
        }

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate int CallbackFunc(
            [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8StringCodec))] string path
        );