C# marhsal 函数返回指向结构数组的指针

C# marhsal function returning pointer to array of structs

有一个 C++ 库包含

像这样:

typedef struct {
    int value1;
    int value2;
    int value3;
} SimpleStruct;

extern SimpleStruct *GetSimpleStructs(int *count);

我在 C# .Net Core 项目中有这个实现:

[StructLayout(LayoutKind.Sequential)]
struct SimpleStruct 
{
    int value1;
    int value2;
    int value3;
}

[DllImport(LibName)]
public static extern IntPtr GetSimpleStructs(out int count);

以及将 IntPtr 转换为 SimpleStruct[] 的函数:

SimpleStruct[] GetSimpleStructs()
{
    var ptrToArray  = GetSimpleStructs(out var count);
    var arrayOfPtrs = new IntPtr[count];
    var resultArray = new SimpleStruct[count];
    Marshal.Copy(ptrToArray, arrayOfPtrs, 0, count);
    for (var i = 0; i < count; i++)
        resultArray[i] = Marshal.PtrToStructure<SimpleStruct>(arrayOfPtrs[i]); // HERE
    return resultArray;
}

// HERE 行之前一切正常。在第一次迭代中,程序以退出代码 139 结束,没有任何异常。

那我做错了什么?

此外,我无法在 DllImport

的项目中引用带有结构的项目

如果您的非托管方法 return 是一个指向结构块的指针,则不需要 IntPtr[] 步骤 - 只有一个指针 (如果您正在 returning SimpleStruct **,那将很有用)。

相反,您首先需要使用明确的布局属性确保布局完全正确(包括大小),然后:只需通过指针:

unsafe
{
    SomeStruct* ptr = (SomeStruct*)ptrToArray.ToPointer();
    var resultArray = new SimpleStruct[count];
    for (int i = 0 ; i < resultArray.Length ; i++)
        resultArray[i] = *ptr[i];
}

如果您乐于使用 span,这会更容易。复制步骤就是:

var resultArray = new Span<SomeStruct>(
    ptrToArray.ToPointer(), count).ToArray();

事实上,您可以 return Span<SomeStruct>Memory<SomeStruct>(后者需要 an extra step, but is possible),以允许安全 C# 代码直接从非托管内存位置访问数据,而无需将其复制到托管内存或分配数组!