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

dk1*_*123 11 c# c++ marshalling

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

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

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

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

somefunction([MarshalAs(UnmanagedType.LPStr) string text) [c#]
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

SS

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

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

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

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

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

SS2

意见:

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

(截图还显示了我使用wcstombs的可怕失败)

注意:我正在使用(http://utfcpp.sourceforge.net/)的utf8标题

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

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

Han*_*ant 13

[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 []来避免所有这些.


Vad*_*man 7

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

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


ken*_*uno 5

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

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

P/调用声明:

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

回调中的用法:

[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
);
Run Code Online (Sandbox Code Playgroud)