【问题标题】:How to marshal an unknown length C++ string to C# using its pointer?如何使用其指针将未知长度的 C++ 字符串编组为 C#?
【发布时间】:2018-07-06 22:20:08
【问题描述】:

我正在尝试将结构中动态分配的 char 数组编组为 C#。该结构有一个指向数组的指针。问题是 char 数组包含多个以空字符结尾的字符串,最后一个字符串以两个连续的空字符结尾。

如果我尝试将其编组为LPStr,我将只获得“列表”中的第一个字符串。

我尝试使用 UnmanagedMemoryStream 但它需要知道数组的长度。

有没有办法在不知道数组长度的情况下将字节作为流读取? (除了使用 n 长度字节缓冲区并不断增加指针,直到找到两个连续的空终止字符)。

【问题讨论】:

  • 这听起来应该计算字符串长度没有问题。
  • 你必须自己编组。将结构成员声明为 IntPtr。使用 Marshal.ReadByte() 循环以复制到 byte[],在 0 处使用 Encoding.Default.GetString() 转换子字符串。直到找到第二个 0。

标签: c# c++ pinvoke marshalling


【解决方案1】:

正如 Hans Passant 所建议的,唯一的方法是Marshal.ReadByte(),所以最后你必须多次读取内存(PtrToStringAnsi 至少读取两次加上我们查找下一个字符串的位置开始)。

public static string[] IntPtrToStringArrayAnsi(IntPtr ptr)
{
    var lst = new List<string>();

    do
    {
        lst.Add(Marshal.PtrToStringAnsi(ptr));

        while (Marshal.ReadByte(ptr) != 0)
        {
            ptr = IntPtr.Add(ptr, 1);
        }

        ptr = IntPtr.Add(ptr, 1);
    }
    while (Marshal.ReadByte(ptr) != 0);

    // See comment of @zneak
    if (lst.Count == 1 && lst[0] == string.Empty)
    {
        return new string[0];
    }

    return lst.ToArray();
}

【讨论】:

  • 注意有一个极端情况,第一个字符串可能是空的。您必须至少循环一次。考虑do-while。
  • @HansPassant 必须同时反转while/do while
  • 我不是OP,但在我看来,如果“第一个字符串”为空(整个字符串为\0\0),那么它应该返回一个空列表。
  • @zneak 我正在使用的 API 声明在这种情况下将返回一个空指针。然而,这是一个通用算法的好调用。
  • @CesarHernandez 最后这种字符串数组格式不支持空字符串:"a\0\0b\0" 将被视为"a\0\0",因此将"\0\0" 修剪为空数组是一致的格式。
【解决方案2】:

另一种版本,可避免对数组进行第二次扫描

private static List<string> IntPtrToStringArrayAnsi(IntPtr ptr)
{
    var lst = new List<string>();
    while (true)
    {
        var str = Marshal.PtrToStringAnsi(ptr);
        if (!string.IsNullOrEmpty(str))
        {
            lst.Add(str);
            ptr += str.Length + 1;
        }
        else
            break;
    }

    return lst.ToArray();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-01
    • 1970-01-01
    • 2018-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    相关资源
    最近更新 更多