Reputation: 8208
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
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
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
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