c#:通常将非托管数组转换为托管列表

时间:2009-06-10 17:47:20

标签: c# generics pinvoke unsafe

我正在处理一组通过动态分配的数组返回数据的本机函数。函数将引用指针作为输入,然后将其指向结果数组。

例如:

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

int extern WINAPI getInfo(result**);

调用之后,'result'指向以null结尾的结果数组*。

我想从这个非托管数组创建一个托管列表。我可以做到以下几点:

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

这是有效的,重新实现我将不得不处理的每种类型的结构(~35)将是乏味和丑陋的。我想要一个对数组中结构类型通用的解决方案。为此,我尝试了:

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

但是这不会编译,因为你不能“获取地址,获取大小,或声明指向托管类型('T')的指针”。

我还尝试在不使用不安全代码的情况下执行此操作,但我遇到了Marshal.Copy()需要知道非托管数组大小的问题。我只能使用不安全的代码来确定这一点,因此在这种情况下使用Marshal.Copy()似乎没有任何好处。

我错过了什么?有人可以建议这个问题的通用方法吗?

2 个答案:

答案 0 :(得分:3)

你可以合理地假设所有指针的大小和表示是相同的(不确定C#规范是否保证这一点,但实际上你会发现它是这种情况)。因此,您可以将T**视为IntPtr*。另外,我没有看到Marshal.Copy如何帮助你,因为它只有内置类型的重载。所以:

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

当你调用它时,你需要一个明确的强制转换为IntPtr*,但至少不会有代码重复。

答案 1 :(得分:0)

你说:

  

Marshal.Copy()需要知道大小   非托管数组。我只能   使用不安全的代码确定这个

您似乎错过了Marshal.SizeOf()

根据您在帖子中提到的内容,这可能足以解决您的问题。 (另外,函数的参数可能需要是Object **而不是T **。)