3

I am dealing with a set of native functions that return data through dynamically-allocated arrays. The functions take a reference pointer as input, then point it to the resulting array.

For example:

typedef struct result
{
   //..Some Members..//
}

int extern WINAPI getInfo(result**);

After the call, 'result' points to a null-terminated array of result*.

I want to create a managed list from this unmanaged array. I can do the following:

struct Result
{
   //..The Same Members..//
}

public static unsafe List<Result> getManagedResultList(Result** unmanagedArray)
{
    List<Result> resultList = new List<Result>();

    while (*unmanagedArray != null)
    {
       resultList.Add(**unmanagedArray);
       ++unmanaged;
    }
    return result;
}

This works, it will be tedious and ugly to reimplement for every type of struct that I'll have to deal with (~35). I'd like a solution that is generic over the type of struct in the array. To that end, I tried:

public static unsafe List<T> unmanagedArrToList<T>(T** unmanagedArray)
{ 
    List<T> result = new List<T>();
    while (*unmanagedArray != null)
    {
        result.Add((**unmanagedArray));
        ++unmanagedArray;
    }
    return result;
}

But that won't compile because you cannot "take the address of, get the size of, or declare a pointer to a managed type('T')".

I also tried to do this without using unsafe code, but I ran into the problem that Marshal.Copy() needs to know the size of the unmanaged array. I could only determine this using unsafe code, so there seemed to be no benefit to using Marshal.Copy() in this case.

What am I missing? Could someone suggest a generic approach to this problem?

2 Answers 2

3

You can make a reasonable assumption that size and representation of all pointers is the same (not sure if C# spec guarantees this, but in practice you'll find it to be the case). So you can treat your T** as IntPtr*. Also, I don't see how Marshal.Copy would help you here, since it only has overloads for built-in types. So:

public static unsafe List<T> unmanagedArrToList<T>(IntPtr* p)
{ 
    List<T> result = new List<T>();
    for (; *p != null; ++p)
    {
        T item = (T)Marshal.PtrToStructure(*p, typeof(T));
        result.Add(item);
    }
    return result;
}

Of course you'll need an explicit cast to IntPtr* whenever you call this, but at least there's no code duplication otherwise.

Sign up to request clarification or add additional context in comments.

Comments

0

You said:

Marshal.Copy() needs to know the size of the unmanaged array. I could only determine this using unsafe code

It seems that you're missing Marshal.SizeOf().

From what you've mentioned in the post, that may be enough to solve your problem. (Also, the parameter of your function may need to be Object** instead of T**.)

3 Comments

Marshal.Copy needs to know how many elements are in the source array, but I don't know that at compile time because the array is dynamically allocated by native code. I don't know any way to determine the number of elements in the unmanaged array without using unsafe code.
Also, object is a managed type. As stated above, you cannot perform unsafe operations on a managed type.
At the worst, you could use an IntPtr for the parameter to your function, then call Marshal.Copy() once for each element in the array, until you've reached the end. You ought to be able to shortcut this by making a copy of the passed-in IntPtr, using Marshal.SizeOf() to determine the correct size, than adding that size in a loop until you find the null entry.

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.