【问题标题】:MarshalAs(UnmanagedType.LPStr) - how does this convert utf-8 strings to char*MarshalAs(UnmanagedType.LPStr) - 这如何将 utf-8 字符串转换为 char*
【发布时间】:2012-10-28 15:46:20
【问题描述】:

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

[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 字符(包括韩语位)。

这是一个截图(包含更多细节):

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

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

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

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

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

观察:

  1. wchar_t* 转换为char* 时出现视觉损失
  2. 由于结果,s8正确显示字符串,我知道我已经将wchar_t*中的utf-8文件内容成功转换为char*
  3. 由于“结果”保留了我直接从文件中获取的字节,但我得到的结果与我通过 c# 得到的结果不同(我使用了同一个文件),我得出的结论是 c# marshal 已通过其他一些程序将文件内容进一步更改为char*

(屏幕截图还显示了我在使用 wcstombs 时的严重失败)

注意:我使用的是来自 (http://utfcpp.sourceforge.net/) 的 utf8 标头

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

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

【问题讨论】:

标签: c# c++ marshalling


【解决方案1】:

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

它没有。托管代码中没有“utf-8 字符串”之类的东西,字符串始终以 utf-16 编码。与 LPStr 之间的编组是使用默认系统代码页完成的。除非您使用代码页 949,否则您在调试器中看到韩文字形非常显着。

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

【讨论】:

  • 感谢您的回复。我意识到我被这个 char* 转换问题迷住了,以至于我盲目地忘记了更简单的 wchar_t[] 实现。
【解决方案2】:

如果您需要编组 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】:

    虽然其他答案是正确的,但 .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);

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

    【讨论】:

    【解决方案4】:

    ICustomMarshaler 可以使用,如果使用 .NET Framework 4.7 之前的版本。

    
            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/Invoke 声明:

            [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
            );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-02
      • 1970-01-01
      • 2013-08-20
      • 2010-09-21
      • 2012-07-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多