Gushchin_D
Gushchin_D

Reputation: 23

C# marhsal function returning pointer to array of structs

There is a C++ library which contains

Like this:

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

extern SimpleStruct *GetSimpleStructs(int *count);

I have this implementation in C# .Net Core project:

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

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

And function that converts IntPtr to 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;
}

Everything works ok before the line with // HERE. In the first iteration program just finishes with exit code 139 without any exceptions.

So what am I doing wrong?

Also, I don't have the ability to reference project with struct in the project with DllImport

Upvotes: 2

Views: 459

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1064184

If your unmanaged method returns a pointer to a block of structs, you don't need the IntPtr[] step - there is only one pointer (that would be useful if you were returning a SimpleStruct **).

Instead, you need to first make sure you have the layout exactly right (including the size), by using explicit layout attributes, then: just go via the pointer:

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

If you are happy to use spans, this can be even easier. The copy step is just:

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

In fact, you could just return Span<SomeStruct> or Memory<SomeStruct> (the latter requires an extra step, but is possible), to allow safe C# code to access the data directly from the unmanaged memory location without ever copying it into managed memory or allocating an array!

Upvotes: 4

Related Questions