Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top