0

I'm trying to marshal a dynamically allocated char array in a struct to C#. The struct has a pointer to the array. The problem is the char array contains multiple null terminated strings and the last string is terminated by two consecutive null chars.

If I try to marshal it as LPStr I will get only de first string in the "list".

I tried using UnmanagedMemoryStream but it requires to know the length of the array.

Is there a way to read the bytes as a stream without knowing the length of the array? (Aside from using a n length byte buffer and keep increasing the pointer until two consecutive null terminating chars are found).

2
  • This sounds like there should be no problem with calculating string length. Commented Jul 6, 2018 at 22:25
  • 3
    You have to marshal it yourself. Declare the struct member as IntPtr. Loop with Marshal.ReadByte() to copy into a byte[], At a 0 use Encoding.Default.GetString() to convert the sub-string. Until you find the second 0. Commented Jul 6, 2018 at 22:26

2 Answers 2

1

As suggested by Hans Passant, the only way is to Marshal.ReadByte(), so in the end you have to read the memory multiple times (PtrToStringAnsi reads it at least twice plus the one we do to find where the next string begins).

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();
}
Sign up to request clarification or add additional context in comments.

5 Comments

Beware that there is a corner-case, the first string might be empty. You must loop at least once. Consider do-while.
@HansPassant Had to invert both while/do while
I'm not OP, but it seems to me that if the "first string" is empty (the entire string is \0\0), then it should return an empty list.
@zneak The API I'm using states that in that case a null pointer will be returned. However it's a good call for a general algorithm.
@CesarHernandez In the end this format of array of strings doesn't support empty strings: "a\0\0b\0" will be considered to be "a\0\0", so trimming "\0\0" to an empty array is consistant to the format.
0

An alternate version, which avoids a second sweep over the array

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.