views:

1030

answers:

2

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?

A: 

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**.)

John Fisher
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.
Odrade
Also, object is a managed type. As stated above, you cannot perform unsafe operations on a managed type.
Odrade
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.
John Fisher
+2  A: 

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.

Pavel Minaev