I have a function in C++ dll with its return is 2-point, as follows:
#include "stdafx.h"
#include <vector>
using namespace std;
double** _stdcall f(int *n)
{
vector<double> t;
vector<double> X;
int i=0, j=0;
do
{
t.push_back(3*i-4);
X.push_back(2*j);
i++;
j++;
}
while (i<15&&j<90);
*n=i;
double** ret = new double*[2];
for (i=0;i<2;i++)
ret[i]=new double[*n];
for (i=0;i<*n;i++)
{
ret[0][i]=t[i];
ret[1][i]=X[i];
}
return ret;
}
Now, I declare this function in C# as follows:
[DllImport("exDP.dll")]
public static extern IntPtr[2] f(ref int n_);
But there is an error for the syntax of this declaration.
I study as topic: How to get return array from function with global variable from C++ dll in C#?
How to declare correctly with this function? Thanks.
Edit:
I fixed the error above, remove "2" (the size of array IntPtr) and it's:
[DllImport("exDP.dll")]
public static extern IntPtr[] f(ref int n_);
Now, all of C# code as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
namespace pABC
{
public partial class frmABC : Form
{
[DllImport("exDP.dll")]
public static extern IntPtr[] f(ref int n_);
public frmABC()
{
InitializeComponent();
}
private void cmdOK_Click(object sender, EventArgs e)
{
int n = 0, i;
IntPtr[] ret = new IntPtr[2];
ret = f(ref n);
double[] t = new double[n];
Marshal.Copy(ret[0], t, 0, n);
double[] X = new double[n];
Marshal.Copy(ret[1], X, 0, n);
MessageBox.Show("X[0]= " + X[0].ToString());
}
}
}
I compile OK. When I run it, an error occurs at the line:
ret = f(ref n);
That is: Cannot marshal 'return value': Invalid managed/unmanaged type combination.
How to fix it and get the results correctly. Thanks.
You should do the same, as you did with one-dimension array.
In fact, C pointer (*
) is just an integer (4-byte in 32-bit OS and 8-byte in 64-bit OS) that is allocated on the stack and points to a memory on a heap that contains your data. C arrays are just sequences of data with elements located in memory one after another. For example, double*
in your code points to an array of double
s.
Now, when you create a multidimensional array (your double**
, for example), you are actually creating an array of pointers to arrays. That means, the pointer (double*)*
actually points to an array of double*
s, and each of them points to an array of double
s.
Well, I guess you already know that :)
Now, regarding interoperation with C#. Your C# code in your case expects a pointer type, that is, IntPtr
. To correctly interop this code, you should yet again return an IntPtr
and use Marshal.Copy
method, as you did before in your previous question.
But now, after the first call of Marshal.Copy
, you will get an array of pointers - that is, IntPtr
. For each of these pointers you should call Marshal.Copy
yet again to get your double
s array.
The code would look some kind like this:
[DllImport("exDP.dll")]
// x and y are the dimensions of the array.
public static extern IntPtr f(ref int x, ref int y);
private void cmdOK_Click(object sender, EventArgs e)
{
int x, y;
IntPtr ret = f(ref x, ref y);
IntPtr[] t = new IntPtr[x];
Marshal.Copy(ret, t, 0, x);
double[][] X = new double[x][y];
for (int i = 0; i < x; i++)
{
Marshal.Copy(t[i], X[i], 0, y);
}
//MessageBox.Show("X[0]= " + X[0].ToString());
}
If you will ever have a three-dimensional array (double***
), you will need to have one more loop, and so on.
Now, regarding the memory leak issue. You can create your arrays in C# before passing them to the C++ code, as the others suggested. But you can also free the memory in C++ by simply exporting another function (let's call it clear
) and passing the original IntPtr
there:
C++:
// x is the first dimension of the array
void __stdcall clear(double** arr, int x)
{
for (int i = 0; i < x; i++)
{
// free all inner arrays
delete[] arr[i];
}
delete[] arr;
}
C#:
[DllImport("exDP.dll")]
public static extern void clear(IntPtr arr, int x);
private void cmdOK_Click(object sender, EventArgs e)
{
int x, y;
IntPtr ret = f(ref x, ref y);
...
for (int i = 0; i < x; i++)
{
Marshal.Copy(t[i], X[i], 0, y);
}
clear(ret, x); // <-- this
}
The C Runtime is aware of the amount of memory it has previously allocated for each of these pointers, so you won't need to worry about the correctness of delete[]
operation. But you must be sure to call this function right after you have Marshal.Copy
ied your arrays, because if you will allocate the new arrays by another call to f()
, this function will free the new ones, and the old ones will stay in memory.
I hope I have cleared some things out so you can now start coding your project :)