简体   繁体   中英

c#: generically convert unmanaged array to managed list

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?

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM