Marshalling Complex nested structures between C and C#

Viewed 57

Im trying to populate a struct containing multiple layers of other structs from c using c#. I am very confused on how to create the structs in c# using marshalling. I have been looking at a lot of different examples but I haven't managed to get it working since the examples seem much more simple then the following structure.

I have tried the following but no luck so far.

The structs in c are defined as the following where I want to create a DATASET object i c#.

struct DATASET {
    uint64_t n_tables;
    uint64_t n_columns;
    uint64_t n_segments;
    uint64_t n_records;
    struct DATATABLE **table;
};

struct DATATABLE {
    unsigned int n_headers;
    uint64_t n_columns;
    uint64_t n_segments;
    uint64_t n_records;
    struct DATASEGMENT **segment;
};

struct DATASEGMENT {
    uint64_t n_rows;
    uint64_t n_columns;
    double **data;
    char *label;
    char *header;
    char **text;
};

I have tried the following in c#

[StructLayout(LayoutKind.Sequential)]
public class DATASET
{
    public ulong n_tables;
    public ulong n_columns;
    public ulong n_segments;
    public ulong n_records;
    public DATATABLE[] table;
}
[StructLayout(LayoutKind.Sequential)]
public class DATATABLE
{
    public uint n_headers;
    public ulong n_columns;
    public ulong n_segments;
    public ulong n_records;
    public DATASEGMENT[] segment;
}
[StructLayout(LayoutKind.Sequential)]
public class DATASEGMENT
{
    public ulong n_rows;
    public ulong n_columns;
    public double[][] data;
    [MarshalAs(UnmanagedType.LPStr)] public string label;
    [MarshalAs(UnmanagedType.LPStr)] public string header;
    [MarshalAs(UnmanagedType.LPStr)] public List<string> text;
}

And for creating the struct in c# i have used

[DllImport("Name_of_dll.dll")]
public static extern void print_dataset(DATASET D, int n_lines);

static void Main(string[] args)
{
    DATATABLE[] table_array = { new DATATABLE { n_headers = 23 }, new DATATABLE { n_headers = 3 } };
    DATASET D = new()
    {
        n_tables = 1,
        n_columns = 3,
        n_segments = 1,
        n_records = 9,
        table = table_array,
    };

    print_dataset(D, 2);
}

For testing the created structure in c# i have used the following function from c

extern __declspec(dllexport) void print_dataset(struct DATASET* D, int n_lines);
void print_dataset(struct DATASET* D, int n_lines)
{
    // Works
    printf_s("n_tables = %" PRIu64 ", n_columns = %" PRIu64 ", n_segments = %" PRIu64 ", n_records = %" PRIu64 "\n", D->n_tables, D->n_columns, D->n_segments, D->n_records);
    // Works
    printf_s("n_lines = %d\n", n_lines);
    // Crashes whes it tries to get n_columns
    printf_s("D->table[0]->n_columns = %" PRIu32 "\n", D->table[0]->n_headers);

    // I haven't gotten this far yet
    for (int i = 0; i < n_lines; i++) {
        printf_s("%d | ", i);
        for (int j = 0; j < D->n_columns; j++) {
            printf_s("%f ", D->table[0]->segment[0]->data[j][i]);
        }
        printf_s("\n");
    }
}

The problem is that it works until i hit the table struct where the program then crashes. The output i get is:

n_tables = 1, n_columns = 3, n_segments = 1, n_records = 9

n_lines = 2

Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Repeat 2 times:

at COMPLEX_STRUCT.Program.print_dataset(C_SHARP.DATASET, Int32)

at COMPLEX_STRUCT.Program.Main(System.String[])

I am still pretty new to marshalling and c# in general and don't really understand what is wrong.

0 Answers
Related