user2023861
user2023861

Reputation: 8208

How do I return a 2D int array from C code to C#?

I am learning how to call C code from my C# code. I want to call a C function that returns a 2D array of ints. This function takes no arguments. Here is the function:

extern "C" _declspec(dllexport) int** intMatrixReturn()
{
    int** A = (int**)malloc(3 * sizeof(int *));
    for(int i = 0; i < 3; i++)
    {
        A[i] = (int*)malloc(3 * sizeof(int));
        for(int j = 0; j < 3; j++)
        {
            A[i][j] = i * j;
        }
    }
    return A;
}

This is how I am trying to access the array in my C# code:

IntPtr ip = intArrayReturn();
int[] iarr = new int[9];

Marshal.Copy(ip, iarr, 0, 9);
foreach (var item in iarr)
{
    Console.WriteLine(item);
}

This is my console output:

1
2
3
4218
86245572
86252624
0
0
0

I assume that my problem is my C# code. How do I read the 2D int array that gets returned from my C function? Also, does the garbage-collector free the memory that holds the 2D array, or should I do that in the C# code?

My apologies if this is a duplicate, but all of the questions I found concerning 2D arrays involve sending them from C# to C, not the other way araound.

Upvotes: 3

Views: 680

Answers (3)

Daniel Bonetti
Daniel Bonetti

Reputation: 2416

I ended up using the suggestion of Paul Michalik. I've used a one-dimensional array. It's not a straightfoward manner but it really works well as a 2D:

C side:

extern "C" {

    struct Matrix
    {
      int size1; // rows number
      int size2; // cols number
      int *data; 
    };

    Matrix* intMatrixReturn(int size1, int size2) {

        Matrix *m = (Matrix*)malloc(sizeof(Matrix) * 1);
        m->data = (int*)malloc(sizeof(int) * size1 * size2);
        m->size1 = size1;
        m->size2 = size2;

        for (int i = 0; i < size1; i++)
        {
            for (int j = 0; j < size2; j++)
            {
                m->data[i*size2+j] = i*size2+j;
            }
        }

        return m;

    }
}

C# side:

    [StructLayout(LayoutKind.Sequential)]
    public struct Matrix
    {
        public int size1;
        public int size2;
        public IntPtr data;
    }

    [DllImport(@"dllname.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr intMatrixReturn(int size1, int size2);

    static void Main(string[] args)
    {
        int size1 = 3; // rows
        int size2 = 3; // cols
        IntPtr p1 = intMatrixReturn(size1, size2);
        Matrix m1 = (Matrix)Marshal.PtrToStructure(p1, typeof(Matrix));
        int[] tmp = new int[m1.size1 * m1.size2];
        IntPtr pd2 = m1.data;
        Marshal.Copy(pd2, tmp, 0, m1.size1 * m1.size2);

        for (int i = 0; i < m1.size1; i++)
        {
            for (int j = 0; j < m1.size2; j++)
            {
                Console.Write(tmp[i * m1.size2 + j] + " ");
            }
            Console.WriteLine();
        }
    }  

Output:

0 1 2
3 4 5
6 7 8

Upvotes: 0

Paul Michalik
Paul Michalik

Reputation: 4381

Don't. Use one dimensional array of proper size on the native side, then everything works out of the box. Otherwise you will need to marshal the native array as array of pointers where each element points to the proper memory chunk. Then, do what evanmcdonnal told you, using the respective overload of Marshal.Copy. With regard to memory deallocation, you are in charge, or better, the native library is: the safe way is to pass the arrays back to native library which takes care of proper deallocation.

Upvotes: 0

evanmcdonnal
evanmcdonnal

Reputation: 48096

You're passing Marshal.Copy a one dimensional array so of course that's what you're going to get back. Additionally that foreach loop won't work with a 2d array.

This is by no means a solution, only a starting point;

1) make iarr a 2d array - int[] iarr = new int[9][9];

2) make your print function a nested for loop -

 for (int i = 0; i < 9; i++)
 {
      for (int j = 0; i < 9; j++)
      {
           Console.WriteLine(iarr[i][j]);
      }
 }

Upvotes: 2

Related Questions