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

txu3uszq  于 2023-05-30  发布在  其他
关注(0)|答案(4)|浏览(457)

题目基本上就是我想问的:
[MarshalAs(UnmanagedType.LPStr)]-如何将utf-8字符串转换为char*?
当我尝试在c#和c++ dll之间通信时,我使用上面的行;更具体地说,在以下之间:

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

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

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

  1. c#字符串已成功转换为char*
    1.所得到的char*适当地反映了监 windows 口中对应的utf-8字符(包括韩文比特)。
    以下是一个截图(更多细节):

如你所见,initialScriptText[0]返回单个byte(char):“B”和char* initialScriptText的内容在VS监 windows 口中正确显示(包括韩语)。
通过char指针,英语似乎保存为每char一个byte,而韩语似乎保存为每char两个字节。(截图中的韩语单词是3个字母,因此保存为6个字节)
这似乎表明,每个“字母”并不是保存在相同大小的容器中,而是根据语言而有所不同。(可能的类型提示?)
我试图在纯C中实现相同的结果:阅读utf-8文件并将结果保存为char*
下面是我尝试读取utf-8文件并在c
中转换为char*的示例:

观察结果:
1.从wchar_t*转换为char*时视觉损失
1.由于结果,s8正确显示字符串,我知道我已经成功地将wchar_t*中的utf-8文件内容转换为char*
1.由于'result'保留了我直接从文件中获取的字节,但我得到的结果与我通过c#得到的结果不同(我使用了相同的文件),因此我得出结论,c# marshal已经通过一些其他过程将文件内容进一步变异为char*
(the截图也显示了我在使用wcstombs时的可怕失败)
注意:我使用的是来自(http://utfcpp.sourceforge.net/)的utf8头
请纠正我的代码/观察中的任何错误。
我希望能够模仿我通过c# marshal得到的结果,但在经历了这一切之后,我意识到我完全被卡住了。有什么想法吗

sqougxex

sqougxex1#

[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[]来避免这一切。

cl25kdpy

cl25kdpy2#

虽然其他答案都是正确的,但.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);
谢谢微软!又一个麻烦不见了。

kmynzznz

kmynzznz3#

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());
    }
}

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

rjee0c154#

如果需要封送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);

相关问题