pinvoke marshalling of 2d multidimensional array of type double as input and output between c# and c++












0














I have the following c# and c++ pinvoke marshalling of 2d multidimensional array of type double matter I'm trying to solve.



I've reviewed the following hit to get what I have currently P/Invoke with arrays of double - marshalling data between C# and C++ .



I've reviewed Marshalling C# Jagged Array to C++ which has a very good scenario match but it's not clear how to go from answer to all aspects of implementation.



My issue, I think if i'm on right path so far, is how I unwind the c++ *outputArray = new double[*outputArrayRows, *outputArrayCols]; that is successfully passed back from DllImport enabled call to the c# IntPtr outputArrayPtr variable into the var outputArray = new double[outputArrayRows, outputArrayCols]; variable I need in order to proceed.



Question = Any insights on if the for loop is the right next step and what extraction syntax I use inside of it?



c++ side of things



extern "C" __declspec(dllexport) void SomeFunction(double** inputArray, int inputArrayRows, int inputArrayCols,
double** outputArray, int* outputArrayRows, int* outputArrayCols)
{
// just initialize the output results for testing purposes no value assignment as of yet
*outputArrayRows = 10;
*outputArrayCols = 2;
*outputArray = new double[*outputArrayRows, *outputArrayCols];
return;
}

extern "C" __declspec(dllexport)DllExport void FreeArray(double** allocatedArrayPtr)
{
delete allocatedArrayPtr;
}


c# side of things



[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
static extern void SomeFunction(double[,] inputArray, int inputArrayRows, int inputArrayCols,
out IntPtr outputArray, out int outputArrayRows, out int outputArrayCols);
[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
static extern void FreeArray(IntPtr allocatedArrayPtr);

[TestMethod]
public void DllImport_SomeFunction_ShouldNotThrowException()
{
var inputArray = new double[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };

IntPtr outputArrayPtr; int outputArrayRows, outputArrayCols;
DllImportUnitTests.SomeFunction(inputArray, inputArray.GetLength(0), inputArray.GetLength(1),
out outputArrayPtr, out outputArrayRows, out outputArrayCols);
var outputArray = new double[outputArrayRows, outputArrayCols];
IntPtr outputArrayPtrArray = new IntPtr[outputArrayRows];
//Marshal.Copy(outputArrayPtr, outputArray, 0, outputArrayRows); // overload for double but not for double[,]
Marshal.Copy(outputArrayPtr, outputArrayPtrArray, 0, outputArrayRows);
FreeArray(outputArrayPtr);
for (var i = 0; i < outputArrayPtrArray.Length; i++)
{
Marshal.Copy(outputArrayPtrArray[i], outputArray[i ???], 0, outputArrayCols);
}

Assert.IsNotNull(outputArray);
}





update containing answer [ / what worked for me ]



Based on comments I updated title to denote this issue has to do with trying to pass and receive a 2d [ / multi-dimensional ] array not a jagged array. That said what became apparent in my tests is that vs17 c++ windows desktop dll project environment only does jagged arrays [ e.g. c++ DllExport double** SomeFunction(double** inputArray, . . . and double** returnArray = new double*[numberOfRows] and c# double dogLegValues = new double[numberOfRows][/* numberOfCols not specified */]; ]. Below i'm adding the c# pinvoke DllImport and c++ function signatures that I was able to get things working with and some of the interesting marshalling code for prepping the 2d array for passing as jagged array and for processing the returned jagged array eventually converting it to 2d array that caller was expecting if that helps others.



c# DllImport statement and comments capturing findings



[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
//static extern /* double */ IntPtr SomeFunction(double inputArray, int inputArrayRows, out int outputArrayRows); // pinvoke can marshal double 1d array input but not output
static extern /* double[,] */ IntPtr SomeFunction(/* double[,] */ IntPtr inputArray, int inputArrayRows, out int outputArrayRows); // pinvoke cannot marshal double[,] 2d array input or output


c++ function signature



#define DllExport extern "C" __declspec(dllexport)
//DllExport double* SomeFunction(double* inputArray, int inputArrayRows, int* outputArrayRows) // using flattened 2d array input and output
DllExport double** SomeFunction(double** inputArray, int inputArrayRows, int* outputArraysRows) // using 2d converted to jagged array [ of arrays ] input and output


c# marshaling code for 2d array flattened into 1d array



int outputArrayRows; const int outputArrayCols = 2;
double inputArrayFlattened = new double[inputArray.Length];
//var index = 0; foreach (var value in inputArray) { inputArrayFlattened[index] = value; index++; } // more concise flattening but adds a stack frame variable
for (var i = 0; i < inputArray.GetLength(0); i++) { for (var j = 0; j < inputArray.GetLength(1); j++) inputArrayFlattened[i * inputArray.GetLength(1) + j] = (double)inputArray.GetValue(i, j); }
IntPtr outputArrayPtr = MyUnitTests.SomeFunction(inputArrayFlattened, inputArray.Length, out dogLegValuesRows);
double outputArray = new double[outputArrayCols]; Marshal.Copy(outputArrayPtr, outputArray, 0, outputArrayCols);


c# marshaling code for 2d array



IntPtr inputArrayPtr = new IntPtr[inputArray.GetLength(0)];
var inputArrayJagged = inputArray.ToJaggedArray();
for (var i = 0; i < inputArrayJagged.Length; i++)
{
IntPtr inputArrayJaggedRowPtr = Marshal.AllocCoTaskMem(sizeof(double) * inputArrayJagged[i].Length);
Marshal.Copy(inputArrayJagged[i], 0, inputArrayJaggedRowPtr, inputArrayJagged[i].Length);
inputArrayPtr[i] = inputArrayJaggedRowPtr;
}
IntPtr outputArrayJaggedPtr = MyUnitTests.SomeFunction(inputArrayPtr, inputArray.GetLength(0), out outputArrayRows);
IntPtr outputArrayJaggedPtrArray = new IntPtr[outputArrayRows];
Marshal.Copy(outputArrayJaggedPtr, outputArrayJaggedPtrArray, 0, outputArrayRows);
//FreeArray(outputArrayJaggedPtr); // doesn't appear we need this given passing result back as return value and no issue when returning 1 row but crashes when returning 2 rows

double outputArray = new double[outputArrayRows][/* outputArrayCols not specified */];
for (var i = 0; i < outputArrayJaggedPtrArray.Length; i++)
{
outputArray[i] = new double[outputArrayCols]; // can't do this with a double[,] 2d array or can you ???
double outputArrayJaggedRow = new double[outputArrayCols];
Marshal.Copy(outputArrayJaggedPtrArray[i], outputArrayJaggedRow, 0, outputArrayCols);
outputArray[i] = outputArrayJaggedRow;
}

var results = outputArray.ToTwoDimensionalArray();


c++ jagged array initialization and assignment examples



// hard coded test return values used to get pinvoke marshalling worked out using flattened 2d array input and output
double* returnArray = new double[2]; // or new double[outputDataCols]
returnArray[0] = 1234.56; returnArray[1] = 98.76; dogLegValuesRows = 1;

// hard coded test return values used to get pinvoke marshalling worked out using 2d converted to jagged array [ of arrays ] input and output
double** returnArray = new double*[2]; // or new double*[*outputDataRows]
returnArray[0] = new double[2]; // or new double[*outputDataCols]
returnArray[0][0] = 1234.56; returnArray[0][1] = 98.76; //*outputDataRows = 1;
returnArray[1] = new double[2]; // or new double[*outputDataCols]
returnArray[1][0] = 7890.12; returnArray[1][1] = 34.56; *outputDataRows = 2;









share|improve this question




















  • 1




    double[,] is a multi-dimensional array, not a jagged array (double is). A C# multi-dimensional array is like a C/C++ multi-dimensional array (binary layout is the same). So, can you clarify what you're after exactly?
    – Simon Mourier
    Nov 22 '18 at 8:56










  • @Simon thanks for pointed that out. Based on your comment and Martin's I realized i'm dealing with trying to pass a 2d array form of multi-dimensional array and receive one as a return value. I think I will update my original post to correct the title and add "update" details commenting on what appears to be interesting parts of making this work.
    – myusrn
    Nov 22 '18 at 11:03
















0














I have the following c# and c++ pinvoke marshalling of 2d multidimensional array of type double matter I'm trying to solve.



I've reviewed the following hit to get what I have currently P/Invoke with arrays of double - marshalling data between C# and C++ .



I've reviewed Marshalling C# Jagged Array to C++ which has a very good scenario match but it's not clear how to go from answer to all aspects of implementation.



My issue, I think if i'm on right path so far, is how I unwind the c++ *outputArray = new double[*outputArrayRows, *outputArrayCols]; that is successfully passed back from DllImport enabled call to the c# IntPtr outputArrayPtr variable into the var outputArray = new double[outputArrayRows, outputArrayCols]; variable I need in order to proceed.



Question = Any insights on if the for loop is the right next step and what extraction syntax I use inside of it?



c++ side of things



extern "C" __declspec(dllexport) void SomeFunction(double** inputArray, int inputArrayRows, int inputArrayCols,
double** outputArray, int* outputArrayRows, int* outputArrayCols)
{
// just initialize the output results for testing purposes no value assignment as of yet
*outputArrayRows = 10;
*outputArrayCols = 2;
*outputArray = new double[*outputArrayRows, *outputArrayCols];
return;
}

extern "C" __declspec(dllexport)DllExport void FreeArray(double** allocatedArrayPtr)
{
delete allocatedArrayPtr;
}


c# side of things



[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
static extern void SomeFunction(double[,] inputArray, int inputArrayRows, int inputArrayCols,
out IntPtr outputArray, out int outputArrayRows, out int outputArrayCols);
[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
static extern void FreeArray(IntPtr allocatedArrayPtr);

[TestMethod]
public void DllImport_SomeFunction_ShouldNotThrowException()
{
var inputArray = new double[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };

IntPtr outputArrayPtr; int outputArrayRows, outputArrayCols;
DllImportUnitTests.SomeFunction(inputArray, inputArray.GetLength(0), inputArray.GetLength(1),
out outputArrayPtr, out outputArrayRows, out outputArrayCols);
var outputArray = new double[outputArrayRows, outputArrayCols];
IntPtr outputArrayPtrArray = new IntPtr[outputArrayRows];
//Marshal.Copy(outputArrayPtr, outputArray, 0, outputArrayRows); // overload for double but not for double[,]
Marshal.Copy(outputArrayPtr, outputArrayPtrArray, 0, outputArrayRows);
FreeArray(outputArrayPtr);
for (var i = 0; i < outputArrayPtrArray.Length; i++)
{
Marshal.Copy(outputArrayPtrArray[i], outputArray[i ???], 0, outputArrayCols);
}

Assert.IsNotNull(outputArray);
}





update containing answer [ / what worked for me ]



Based on comments I updated title to denote this issue has to do with trying to pass and receive a 2d [ / multi-dimensional ] array not a jagged array. That said what became apparent in my tests is that vs17 c++ windows desktop dll project environment only does jagged arrays [ e.g. c++ DllExport double** SomeFunction(double** inputArray, . . . and double** returnArray = new double*[numberOfRows] and c# double dogLegValues = new double[numberOfRows][/* numberOfCols not specified */]; ]. Below i'm adding the c# pinvoke DllImport and c++ function signatures that I was able to get things working with and some of the interesting marshalling code for prepping the 2d array for passing as jagged array and for processing the returned jagged array eventually converting it to 2d array that caller was expecting if that helps others.



c# DllImport statement and comments capturing findings



[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
//static extern /* double */ IntPtr SomeFunction(double inputArray, int inputArrayRows, out int outputArrayRows); // pinvoke can marshal double 1d array input but not output
static extern /* double[,] */ IntPtr SomeFunction(/* double[,] */ IntPtr inputArray, int inputArrayRows, out int outputArrayRows); // pinvoke cannot marshal double[,] 2d array input or output


c++ function signature



#define DllExport extern "C" __declspec(dllexport)
//DllExport double* SomeFunction(double* inputArray, int inputArrayRows, int* outputArrayRows) // using flattened 2d array input and output
DllExport double** SomeFunction(double** inputArray, int inputArrayRows, int* outputArraysRows) // using 2d converted to jagged array [ of arrays ] input and output


c# marshaling code for 2d array flattened into 1d array



int outputArrayRows; const int outputArrayCols = 2;
double inputArrayFlattened = new double[inputArray.Length];
//var index = 0; foreach (var value in inputArray) { inputArrayFlattened[index] = value; index++; } // more concise flattening but adds a stack frame variable
for (var i = 0; i < inputArray.GetLength(0); i++) { for (var j = 0; j < inputArray.GetLength(1); j++) inputArrayFlattened[i * inputArray.GetLength(1) + j] = (double)inputArray.GetValue(i, j); }
IntPtr outputArrayPtr = MyUnitTests.SomeFunction(inputArrayFlattened, inputArray.Length, out dogLegValuesRows);
double outputArray = new double[outputArrayCols]; Marshal.Copy(outputArrayPtr, outputArray, 0, outputArrayCols);


c# marshaling code for 2d array



IntPtr inputArrayPtr = new IntPtr[inputArray.GetLength(0)];
var inputArrayJagged = inputArray.ToJaggedArray();
for (var i = 0; i < inputArrayJagged.Length; i++)
{
IntPtr inputArrayJaggedRowPtr = Marshal.AllocCoTaskMem(sizeof(double) * inputArrayJagged[i].Length);
Marshal.Copy(inputArrayJagged[i], 0, inputArrayJaggedRowPtr, inputArrayJagged[i].Length);
inputArrayPtr[i] = inputArrayJaggedRowPtr;
}
IntPtr outputArrayJaggedPtr = MyUnitTests.SomeFunction(inputArrayPtr, inputArray.GetLength(0), out outputArrayRows);
IntPtr outputArrayJaggedPtrArray = new IntPtr[outputArrayRows];
Marshal.Copy(outputArrayJaggedPtr, outputArrayJaggedPtrArray, 0, outputArrayRows);
//FreeArray(outputArrayJaggedPtr); // doesn't appear we need this given passing result back as return value and no issue when returning 1 row but crashes when returning 2 rows

double outputArray = new double[outputArrayRows][/* outputArrayCols not specified */];
for (var i = 0; i < outputArrayJaggedPtrArray.Length; i++)
{
outputArray[i] = new double[outputArrayCols]; // can't do this with a double[,] 2d array or can you ???
double outputArrayJaggedRow = new double[outputArrayCols];
Marshal.Copy(outputArrayJaggedPtrArray[i], outputArrayJaggedRow, 0, outputArrayCols);
outputArray[i] = outputArrayJaggedRow;
}

var results = outputArray.ToTwoDimensionalArray();


c++ jagged array initialization and assignment examples



// hard coded test return values used to get pinvoke marshalling worked out using flattened 2d array input and output
double* returnArray = new double[2]; // or new double[outputDataCols]
returnArray[0] = 1234.56; returnArray[1] = 98.76; dogLegValuesRows = 1;

// hard coded test return values used to get pinvoke marshalling worked out using 2d converted to jagged array [ of arrays ] input and output
double** returnArray = new double*[2]; // or new double*[*outputDataRows]
returnArray[0] = new double[2]; // or new double[*outputDataCols]
returnArray[0][0] = 1234.56; returnArray[0][1] = 98.76; //*outputDataRows = 1;
returnArray[1] = new double[2]; // or new double[*outputDataCols]
returnArray[1][0] = 7890.12; returnArray[1][1] = 34.56; *outputDataRows = 2;









share|improve this question




















  • 1




    double[,] is a multi-dimensional array, not a jagged array (double is). A C# multi-dimensional array is like a C/C++ multi-dimensional array (binary layout is the same). So, can you clarify what you're after exactly?
    – Simon Mourier
    Nov 22 '18 at 8:56










  • @Simon thanks for pointed that out. Based on your comment and Martin's I realized i'm dealing with trying to pass a 2d array form of multi-dimensional array and receive one as a return value. I think I will update my original post to correct the title and add "update" details commenting on what appears to be interesting parts of making this work.
    – myusrn
    Nov 22 '18 at 11:03














0












0








0


0





I have the following c# and c++ pinvoke marshalling of 2d multidimensional array of type double matter I'm trying to solve.



I've reviewed the following hit to get what I have currently P/Invoke with arrays of double - marshalling data between C# and C++ .



I've reviewed Marshalling C# Jagged Array to C++ which has a very good scenario match but it's not clear how to go from answer to all aspects of implementation.



My issue, I think if i'm on right path so far, is how I unwind the c++ *outputArray = new double[*outputArrayRows, *outputArrayCols]; that is successfully passed back from DllImport enabled call to the c# IntPtr outputArrayPtr variable into the var outputArray = new double[outputArrayRows, outputArrayCols]; variable I need in order to proceed.



Question = Any insights on if the for loop is the right next step and what extraction syntax I use inside of it?



c++ side of things



extern "C" __declspec(dllexport) void SomeFunction(double** inputArray, int inputArrayRows, int inputArrayCols,
double** outputArray, int* outputArrayRows, int* outputArrayCols)
{
// just initialize the output results for testing purposes no value assignment as of yet
*outputArrayRows = 10;
*outputArrayCols = 2;
*outputArray = new double[*outputArrayRows, *outputArrayCols];
return;
}

extern "C" __declspec(dllexport)DllExport void FreeArray(double** allocatedArrayPtr)
{
delete allocatedArrayPtr;
}


c# side of things



[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
static extern void SomeFunction(double[,] inputArray, int inputArrayRows, int inputArrayCols,
out IntPtr outputArray, out int outputArrayRows, out int outputArrayCols);
[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
static extern void FreeArray(IntPtr allocatedArrayPtr);

[TestMethod]
public void DllImport_SomeFunction_ShouldNotThrowException()
{
var inputArray = new double[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };

IntPtr outputArrayPtr; int outputArrayRows, outputArrayCols;
DllImportUnitTests.SomeFunction(inputArray, inputArray.GetLength(0), inputArray.GetLength(1),
out outputArrayPtr, out outputArrayRows, out outputArrayCols);
var outputArray = new double[outputArrayRows, outputArrayCols];
IntPtr outputArrayPtrArray = new IntPtr[outputArrayRows];
//Marshal.Copy(outputArrayPtr, outputArray, 0, outputArrayRows); // overload for double but not for double[,]
Marshal.Copy(outputArrayPtr, outputArrayPtrArray, 0, outputArrayRows);
FreeArray(outputArrayPtr);
for (var i = 0; i < outputArrayPtrArray.Length; i++)
{
Marshal.Copy(outputArrayPtrArray[i], outputArray[i ???], 0, outputArrayCols);
}

Assert.IsNotNull(outputArray);
}





update containing answer [ / what worked for me ]



Based on comments I updated title to denote this issue has to do with trying to pass and receive a 2d [ / multi-dimensional ] array not a jagged array. That said what became apparent in my tests is that vs17 c++ windows desktop dll project environment only does jagged arrays [ e.g. c++ DllExport double** SomeFunction(double** inputArray, . . . and double** returnArray = new double*[numberOfRows] and c# double dogLegValues = new double[numberOfRows][/* numberOfCols not specified */]; ]. Below i'm adding the c# pinvoke DllImport and c++ function signatures that I was able to get things working with and some of the interesting marshalling code for prepping the 2d array for passing as jagged array and for processing the returned jagged array eventually converting it to 2d array that caller was expecting if that helps others.



c# DllImport statement and comments capturing findings



[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
//static extern /* double */ IntPtr SomeFunction(double inputArray, int inputArrayRows, out int outputArrayRows); // pinvoke can marshal double 1d array input but not output
static extern /* double[,] */ IntPtr SomeFunction(/* double[,] */ IntPtr inputArray, int inputArrayRows, out int outputArrayRows); // pinvoke cannot marshal double[,] 2d array input or output


c++ function signature



#define DllExport extern "C" __declspec(dllexport)
//DllExport double* SomeFunction(double* inputArray, int inputArrayRows, int* outputArrayRows) // using flattened 2d array input and output
DllExport double** SomeFunction(double** inputArray, int inputArrayRows, int* outputArraysRows) // using 2d converted to jagged array [ of arrays ] input and output


c# marshaling code for 2d array flattened into 1d array



int outputArrayRows; const int outputArrayCols = 2;
double inputArrayFlattened = new double[inputArray.Length];
//var index = 0; foreach (var value in inputArray) { inputArrayFlattened[index] = value; index++; } // more concise flattening but adds a stack frame variable
for (var i = 0; i < inputArray.GetLength(0); i++) { for (var j = 0; j < inputArray.GetLength(1); j++) inputArrayFlattened[i * inputArray.GetLength(1) + j] = (double)inputArray.GetValue(i, j); }
IntPtr outputArrayPtr = MyUnitTests.SomeFunction(inputArrayFlattened, inputArray.Length, out dogLegValuesRows);
double outputArray = new double[outputArrayCols]; Marshal.Copy(outputArrayPtr, outputArray, 0, outputArrayCols);


c# marshaling code for 2d array



IntPtr inputArrayPtr = new IntPtr[inputArray.GetLength(0)];
var inputArrayJagged = inputArray.ToJaggedArray();
for (var i = 0; i < inputArrayJagged.Length; i++)
{
IntPtr inputArrayJaggedRowPtr = Marshal.AllocCoTaskMem(sizeof(double) * inputArrayJagged[i].Length);
Marshal.Copy(inputArrayJagged[i], 0, inputArrayJaggedRowPtr, inputArrayJagged[i].Length);
inputArrayPtr[i] = inputArrayJaggedRowPtr;
}
IntPtr outputArrayJaggedPtr = MyUnitTests.SomeFunction(inputArrayPtr, inputArray.GetLength(0), out outputArrayRows);
IntPtr outputArrayJaggedPtrArray = new IntPtr[outputArrayRows];
Marshal.Copy(outputArrayJaggedPtr, outputArrayJaggedPtrArray, 0, outputArrayRows);
//FreeArray(outputArrayJaggedPtr); // doesn't appear we need this given passing result back as return value and no issue when returning 1 row but crashes when returning 2 rows

double outputArray = new double[outputArrayRows][/* outputArrayCols not specified */];
for (var i = 0; i < outputArrayJaggedPtrArray.Length; i++)
{
outputArray[i] = new double[outputArrayCols]; // can't do this with a double[,] 2d array or can you ???
double outputArrayJaggedRow = new double[outputArrayCols];
Marshal.Copy(outputArrayJaggedPtrArray[i], outputArrayJaggedRow, 0, outputArrayCols);
outputArray[i] = outputArrayJaggedRow;
}

var results = outputArray.ToTwoDimensionalArray();


c++ jagged array initialization and assignment examples



// hard coded test return values used to get pinvoke marshalling worked out using flattened 2d array input and output
double* returnArray = new double[2]; // or new double[outputDataCols]
returnArray[0] = 1234.56; returnArray[1] = 98.76; dogLegValuesRows = 1;

// hard coded test return values used to get pinvoke marshalling worked out using 2d converted to jagged array [ of arrays ] input and output
double** returnArray = new double*[2]; // or new double*[*outputDataRows]
returnArray[0] = new double[2]; // or new double[*outputDataCols]
returnArray[0][0] = 1234.56; returnArray[0][1] = 98.76; //*outputDataRows = 1;
returnArray[1] = new double[2]; // or new double[*outputDataCols]
returnArray[1][0] = 7890.12; returnArray[1][1] = 34.56; *outputDataRows = 2;









share|improve this question















I have the following c# and c++ pinvoke marshalling of 2d multidimensional array of type double matter I'm trying to solve.



I've reviewed the following hit to get what I have currently P/Invoke with arrays of double - marshalling data between C# and C++ .



I've reviewed Marshalling C# Jagged Array to C++ which has a very good scenario match but it's not clear how to go from answer to all aspects of implementation.



My issue, I think if i'm on right path so far, is how I unwind the c++ *outputArray = new double[*outputArrayRows, *outputArrayCols]; that is successfully passed back from DllImport enabled call to the c# IntPtr outputArrayPtr variable into the var outputArray = new double[outputArrayRows, outputArrayCols]; variable I need in order to proceed.



Question = Any insights on if the for loop is the right next step and what extraction syntax I use inside of it?



c++ side of things



extern "C" __declspec(dllexport) void SomeFunction(double** inputArray, int inputArrayRows, int inputArrayCols,
double** outputArray, int* outputArrayRows, int* outputArrayCols)
{
// just initialize the output results for testing purposes no value assignment as of yet
*outputArrayRows = 10;
*outputArrayCols = 2;
*outputArray = new double[*outputArrayRows, *outputArrayCols];
return;
}

extern "C" __declspec(dllexport)DllExport void FreeArray(double** allocatedArrayPtr)
{
delete allocatedArrayPtr;
}


c# side of things



[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
static extern void SomeFunction(double[,] inputArray, int inputArrayRows, int inputArrayCols,
out IntPtr outputArray, out int outputArrayRows, out int outputArrayCols);
[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
static extern void FreeArray(IntPtr allocatedArrayPtr);

[TestMethod]
public void DllImport_SomeFunction_ShouldNotThrowException()
{
var inputArray = new double[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };

IntPtr outputArrayPtr; int outputArrayRows, outputArrayCols;
DllImportUnitTests.SomeFunction(inputArray, inputArray.GetLength(0), inputArray.GetLength(1),
out outputArrayPtr, out outputArrayRows, out outputArrayCols);
var outputArray = new double[outputArrayRows, outputArrayCols];
IntPtr outputArrayPtrArray = new IntPtr[outputArrayRows];
//Marshal.Copy(outputArrayPtr, outputArray, 0, outputArrayRows); // overload for double but not for double[,]
Marshal.Copy(outputArrayPtr, outputArrayPtrArray, 0, outputArrayRows);
FreeArray(outputArrayPtr);
for (var i = 0; i < outputArrayPtrArray.Length; i++)
{
Marshal.Copy(outputArrayPtrArray[i], outputArray[i ???], 0, outputArrayCols);
}

Assert.IsNotNull(outputArray);
}





update containing answer [ / what worked for me ]



Based on comments I updated title to denote this issue has to do with trying to pass and receive a 2d [ / multi-dimensional ] array not a jagged array. That said what became apparent in my tests is that vs17 c++ windows desktop dll project environment only does jagged arrays [ e.g. c++ DllExport double** SomeFunction(double** inputArray, . . . and double** returnArray = new double*[numberOfRows] and c# double dogLegValues = new double[numberOfRows][/* numberOfCols not specified */]; ]. Below i'm adding the c# pinvoke DllImport and c++ function signatures that I was able to get things working with and some of the interesting marshalling code for prepping the 2d array for passing as jagged array and for processing the returned jagged array eventually converting it to 2d array that caller was expecting if that helps others.



c# DllImport statement and comments capturing findings



[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
//static extern /* double */ IntPtr SomeFunction(double inputArray, int inputArrayRows, out int outputArrayRows); // pinvoke can marshal double 1d array input but not output
static extern /* double[,] */ IntPtr SomeFunction(/* double[,] */ IntPtr inputArray, int inputArrayRows, out int outputArrayRows); // pinvoke cannot marshal double[,] 2d array input or output


c++ function signature



#define DllExport extern "C" __declspec(dllexport)
//DllExport double* SomeFunction(double* inputArray, int inputArrayRows, int* outputArrayRows) // using flattened 2d array input and output
DllExport double** SomeFunction(double** inputArray, int inputArrayRows, int* outputArraysRows) // using 2d converted to jagged array [ of arrays ] input and output


c# marshaling code for 2d array flattened into 1d array



int outputArrayRows; const int outputArrayCols = 2;
double inputArrayFlattened = new double[inputArray.Length];
//var index = 0; foreach (var value in inputArray) { inputArrayFlattened[index] = value; index++; } // more concise flattening but adds a stack frame variable
for (var i = 0; i < inputArray.GetLength(0); i++) { for (var j = 0; j < inputArray.GetLength(1); j++) inputArrayFlattened[i * inputArray.GetLength(1) + j] = (double)inputArray.GetValue(i, j); }
IntPtr outputArrayPtr = MyUnitTests.SomeFunction(inputArrayFlattened, inputArray.Length, out dogLegValuesRows);
double outputArray = new double[outputArrayCols]; Marshal.Copy(outputArrayPtr, outputArray, 0, outputArrayCols);


c# marshaling code for 2d array



IntPtr inputArrayPtr = new IntPtr[inputArray.GetLength(0)];
var inputArrayJagged = inputArray.ToJaggedArray();
for (var i = 0; i < inputArrayJagged.Length; i++)
{
IntPtr inputArrayJaggedRowPtr = Marshal.AllocCoTaskMem(sizeof(double) * inputArrayJagged[i].Length);
Marshal.Copy(inputArrayJagged[i], 0, inputArrayJaggedRowPtr, inputArrayJagged[i].Length);
inputArrayPtr[i] = inputArrayJaggedRowPtr;
}
IntPtr outputArrayJaggedPtr = MyUnitTests.SomeFunction(inputArrayPtr, inputArray.GetLength(0), out outputArrayRows);
IntPtr outputArrayJaggedPtrArray = new IntPtr[outputArrayRows];
Marshal.Copy(outputArrayJaggedPtr, outputArrayJaggedPtrArray, 0, outputArrayRows);
//FreeArray(outputArrayJaggedPtr); // doesn't appear we need this given passing result back as return value and no issue when returning 1 row but crashes when returning 2 rows

double outputArray = new double[outputArrayRows][/* outputArrayCols not specified */];
for (var i = 0; i < outputArrayJaggedPtrArray.Length; i++)
{
outputArray[i] = new double[outputArrayCols]; // can't do this with a double[,] 2d array or can you ???
double outputArrayJaggedRow = new double[outputArrayCols];
Marshal.Copy(outputArrayJaggedPtrArray[i], outputArrayJaggedRow, 0, outputArrayCols);
outputArray[i] = outputArrayJaggedRow;
}

var results = outputArray.ToTwoDimensionalArray();


c++ jagged array initialization and assignment examples



// hard coded test return values used to get pinvoke marshalling worked out using flattened 2d array input and output
double* returnArray = new double[2]; // or new double[outputDataCols]
returnArray[0] = 1234.56; returnArray[1] = 98.76; dogLegValuesRows = 1;

// hard coded test return values used to get pinvoke marshalling worked out using 2d converted to jagged array [ of arrays ] input and output
double** returnArray = new double*[2]; // or new double*[*outputDataRows]
returnArray[0] = new double[2]; // or new double[*outputDataCols]
returnArray[0][0] = 1234.56; returnArray[0][1] = 98.76; //*outputDataRows = 1;
returnArray[1] = new double[2]; // or new double[*outputDataCols]
returnArray[1][0] = 7890.12; returnArray[1][1] = 34.56; *outputDataRows = 2;






c# c++ interop pinvoke marshalling






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 25 '18 at 2:32







myusrn

















asked Nov 21 '18 at 19:35









myusrnmyusrn

175215




175215








  • 1




    double[,] is a multi-dimensional array, not a jagged array (double is). A C# multi-dimensional array is like a C/C++ multi-dimensional array (binary layout is the same). So, can you clarify what you're after exactly?
    – Simon Mourier
    Nov 22 '18 at 8:56










  • @Simon thanks for pointed that out. Based on your comment and Martin's I realized i'm dealing with trying to pass a 2d array form of multi-dimensional array and receive one as a return value. I think I will update my original post to correct the title and add "update" details commenting on what appears to be interesting parts of making this work.
    – myusrn
    Nov 22 '18 at 11:03














  • 1




    double[,] is a multi-dimensional array, not a jagged array (double is). A C# multi-dimensional array is like a C/C++ multi-dimensional array (binary layout is the same). So, can you clarify what you're after exactly?
    – Simon Mourier
    Nov 22 '18 at 8:56










  • @Simon thanks for pointed that out. Based on your comment and Martin's I realized i'm dealing with trying to pass a 2d array form of multi-dimensional array and receive one as a return value. I think I will update my original post to correct the title and add "update" details commenting on what appears to be interesting parts of making this work.
    – myusrn
    Nov 22 '18 at 11:03








1




1




double[,] is a multi-dimensional array, not a jagged array (double is). A C# multi-dimensional array is like a C/C++ multi-dimensional array (binary layout is the same). So, can you clarify what you're after exactly?
– Simon Mourier
Nov 22 '18 at 8:56




double[,] is a multi-dimensional array, not a jagged array (double is). A C# multi-dimensional array is like a C/C++ multi-dimensional array (binary layout is the same). So, can you clarify what you're after exactly?
– Simon Mourier
Nov 22 '18 at 8:56












@Simon thanks for pointed that out. Based on your comment and Martin's I realized i'm dealing with trying to pass a 2d array form of multi-dimensional array and receive one as a return value. I think I will update my original post to correct the title and add "update" details commenting on what appears to be interesting parts of making this work.
– myusrn
Nov 22 '18 at 11:03




@Simon thanks for pointed that out. Based on your comment and Martin's I realized i'm dealing with trying to pass a 2d array form of multi-dimensional array and receive one as a return value. I think I will update my original post to correct the title and add "update" details commenting on what appears to be interesting parts of making this work.
– myusrn
Nov 22 '18 at 11:03












1 Answer
1






active

oldest

votes


















1














This code:



*values = new double[*valuesOuterLen, *valuesInnerLen];


does not do what you think it does. (I shortened the variable names because they just complicate things.)



C++ inherits from C a lack of multi-dimensional arrays. What it does have is arrays of arrays, or arrays of pointers to arrays. In both cases you index these as array[firstIndex][secondIndex]. You can't new an array of pointers like that, you have to write something like:



double*** values;  // Triple pointer!  Ow!!  *values is a pointer to
// (an array of) pointers to (arrays of) doubles.
*values = new double*[*valuesOuterLen];
for (size_t i=0; i<valuesOuterLen; i++) {
(*values)[i] = new double[*valuesInnerLen];
}


What you have actually invoked is the C++ comma operator, which evaluates and discards the first operand, and then evaluates the second operand. Crank your compiler warnings up; a good compiler will warn that the first operand has no side-effects.






share|improve this answer



















  • 1




    @myusrn Flattening the array to 1D sounds like an excellent idea.
    – Martin Bonner
    Nov 22 '18 at 9:42










  • I have things now working. what helped was your comment about not being able to new an array of pointers as I was doing in example and what c++ actually doesn't with that statement that. what this made me realize, confirmed through testing, is that pinvoke can only pass and accept as a return jagged arrays vs 2d arrays. This also led to more simplified DllImport statements and some interesting marshalling code to prepare for passing the 2d array converted to jagged array and to process returned jagged array converted to 2d array.
    – myusrn
    Nov 22 '18 at 10:59










  • i'm wanting to mark your response as the answer as it led to me getting things to work but as you can see in my "update" section of my original ask which has code associated with what ended up working it seems "double ** values" worked and not "double*** values" wasn't working for me. not sure why that was.
    – myusrn
    Nov 22 '18 at 17:34











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53419367%2fpinvoke-marshalling-of-2d-multidimensional-array-of-type-double-as-input-and-out%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














This code:



*values = new double[*valuesOuterLen, *valuesInnerLen];


does not do what you think it does. (I shortened the variable names because they just complicate things.)



C++ inherits from C a lack of multi-dimensional arrays. What it does have is arrays of arrays, or arrays of pointers to arrays. In both cases you index these as array[firstIndex][secondIndex]. You can't new an array of pointers like that, you have to write something like:



double*** values;  // Triple pointer!  Ow!!  *values is a pointer to
// (an array of) pointers to (arrays of) doubles.
*values = new double*[*valuesOuterLen];
for (size_t i=0; i<valuesOuterLen; i++) {
(*values)[i] = new double[*valuesInnerLen];
}


What you have actually invoked is the C++ comma operator, which evaluates and discards the first operand, and then evaluates the second operand. Crank your compiler warnings up; a good compiler will warn that the first operand has no side-effects.






share|improve this answer



















  • 1




    @myusrn Flattening the array to 1D sounds like an excellent idea.
    – Martin Bonner
    Nov 22 '18 at 9:42










  • I have things now working. what helped was your comment about not being able to new an array of pointers as I was doing in example and what c++ actually doesn't with that statement that. what this made me realize, confirmed through testing, is that pinvoke can only pass and accept as a return jagged arrays vs 2d arrays. This also led to more simplified DllImport statements and some interesting marshalling code to prepare for passing the 2d array converted to jagged array and to process returned jagged array converted to 2d array.
    – myusrn
    Nov 22 '18 at 10:59










  • i'm wanting to mark your response as the answer as it led to me getting things to work but as you can see in my "update" section of my original ask which has code associated with what ended up working it seems "double ** values" worked and not "double*** values" wasn't working for me. not sure why that was.
    – myusrn
    Nov 22 '18 at 17:34
















1














This code:



*values = new double[*valuesOuterLen, *valuesInnerLen];


does not do what you think it does. (I shortened the variable names because they just complicate things.)



C++ inherits from C a lack of multi-dimensional arrays. What it does have is arrays of arrays, or arrays of pointers to arrays. In both cases you index these as array[firstIndex][secondIndex]. You can't new an array of pointers like that, you have to write something like:



double*** values;  // Triple pointer!  Ow!!  *values is a pointer to
// (an array of) pointers to (arrays of) doubles.
*values = new double*[*valuesOuterLen];
for (size_t i=0; i<valuesOuterLen; i++) {
(*values)[i] = new double[*valuesInnerLen];
}


What you have actually invoked is the C++ comma operator, which evaluates and discards the first operand, and then evaluates the second operand. Crank your compiler warnings up; a good compiler will warn that the first operand has no side-effects.






share|improve this answer



















  • 1




    @myusrn Flattening the array to 1D sounds like an excellent idea.
    – Martin Bonner
    Nov 22 '18 at 9:42










  • I have things now working. what helped was your comment about not being able to new an array of pointers as I was doing in example and what c++ actually doesn't with that statement that. what this made me realize, confirmed through testing, is that pinvoke can only pass and accept as a return jagged arrays vs 2d arrays. This also led to more simplified DllImport statements and some interesting marshalling code to prepare for passing the 2d array converted to jagged array and to process returned jagged array converted to 2d array.
    – myusrn
    Nov 22 '18 at 10:59










  • i'm wanting to mark your response as the answer as it led to me getting things to work but as you can see in my "update" section of my original ask which has code associated with what ended up working it seems "double ** values" worked and not "double*** values" wasn't working for me. not sure why that was.
    – myusrn
    Nov 22 '18 at 17:34














1












1








1






This code:



*values = new double[*valuesOuterLen, *valuesInnerLen];


does not do what you think it does. (I shortened the variable names because they just complicate things.)



C++ inherits from C a lack of multi-dimensional arrays. What it does have is arrays of arrays, or arrays of pointers to arrays. In both cases you index these as array[firstIndex][secondIndex]. You can't new an array of pointers like that, you have to write something like:



double*** values;  // Triple pointer!  Ow!!  *values is a pointer to
// (an array of) pointers to (arrays of) doubles.
*values = new double*[*valuesOuterLen];
for (size_t i=0; i<valuesOuterLen; i++) {
(*values)[i] = new double[*valuesInnerLen];
}


What you have actually invoked is the C++ comma operator, which evaluates and discards the first operand, and then evaluates the second operand. Crank your compiler warnings up; a good compiler will warn that the first operand has no side-effects.






share|improve this answer














This code:



*values = new double[*valuesOuterLen, *valuesInnerLen];


does not do what you think it does. (I shortened the variable names because they just complicate things.)



C++ inherits from C a lack of multi-dimensional arrays. What it does have is arrays of arrays, or arrays of pointers to arrays. In both cases you index these as array[firstIndex][secondIndex]. You can't new an array of pointers like that, you have to write something like:



double*** values;  // Triple pointer!  Ow!!  *values is a pointer to
// (an array of) pointers to (arrays of) doubles.
*values = new double*[*valuesOuterLen];
for (size_t i=0; i<valuesOuterLen; i++) {
(*values)[i] = new double[*valuesInnerLen];
}


What you have actually invoked is the C++ comma operator, which evaluates and discards the first operand, and then evaluates the second operand. Crank your compiler warnings up; a good compiler will warn that the first operand has no side-effects.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 22 '18 at 9:41

























answered Nov 21 '18 at 20:01









Martin BonnerMartin Bonner

23k33161




23k33161








  • 1




    @myusrn Flattening the array to 1D sounds like an excellent idea.
    – Martin Bonner
    Nov 22 '18 at 9:42










  • I have things now working. what helped was your comment about not being able to new an array of pointers as I was doing in example and what c++ actually doesn't with that statement that. what this made me realize, confirmed through testing, is that pinvoke can only pass and accept as a return jagged arrays vs 2d arrays. This also led to more simplified DllImport statements and some interesting marshalling code to prepare for passing the 2d array converted to jagged array and to process returned jagged array converted to 2d array.
    – myusrn
    Nov 22 '18 at 10:59










  • i'm wanting to mark your response as the answer as it led to me getting things to work but as you can see in my "update" section of my original ask which has code associated with what ended up working it seems "double ** values" worked and not "double*** values" wasn't working for me. not sure why that was.
    – myusrn
    Nov 22 '18 at 17:34














  • 1




    @myusrn Flattening the array to 1D sounds like an excellent idea.
    – Martin Bonner
    Nov 22 '18 at 9:42










  • I have things now working. what helped was your comment about not being able to new an array of pointers as I was doing in example and what c++ actually doesn't with that statement that. what this made me realize, confirmed through testing, is that pinvoke can only pass and accept as a return jagged arrays vs 2d arrays. This also led to more simplified DllImport statements and some interesting marshalling code to prepare for passing the 2d array converted to jagged array and to process returned jagged array converted to 2d array.
    – myusrn
    Nov 22 '18 at 10:59










  • i'm wanting to mark your response as the answer as it led to me getting things to work but as you can see in my "update" section of my original ask which has code associated with what ended up working it seems "double ** values" worked and not "double*** values" wasn't working for me. not sure why that was.
    – myusrn
    Nov 22 '18 at 17:34








1




1




@myusrn Flattening the array to 1D sounds like an excellent idea.
– Martin Bonner
Nov 22 '18 at 9:42




@myusrn Flattening the array to 1D sounds like an excellent idea.
– Martin Bonner
Nov 22 '18 at 9:42












I have things now working. what helped was your comment about not being able to new an array of pointers as I was doing in example and what c++ actually doesn't with that statement that. what this made me realize, confirmed through testing, is that pinvoke can only pass and accept as a return jagged arrays vs 2d arrays. This also led to more simplified DllImport statements and some interesting marshalling code to prepare for passing the 2d array converted to jagged array and to process returned jagged array converted to 2d array.
– myusrn
Nov 22 '18 at 10:59




I have things now working. what helped was your comment about not being able to new an array of pointers as I was doing in example and what c++ actually doesn't with that statement that. what this made me realize, confirmed through testing, is that pinvoke can only pass and accept as a return jagged arrays vs 2d arrays. This also led to more simplified DllImport statements and some interesting marshalling code to prepare for passing the 2d array converted to jagged array and to process returned jagged array converted to 2d array.
– myusrn
Nov 22 '18 at 10:59












i'm wanting to mark your response as the answer as it led to me getting things to work but as you can see in my "update" section of my original ask which has code associated with what ended up working it seems "double ** values" worked and not "double*** values" wasn't working for me. not sure why that was.
– myusrn
Nov 22 '18 at 17:34




i'm wanting to mark your response as the answer as it led to me getting things to work but as you can see in my "update" section of my original ask which has code associated with what ended up working it seems "double ** values" worked and not "double*** values" wasn't working for me. not sure why that was.
– myusrn
Nov 22 '18 at 17:34


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53419367%2fpinvoke-marshalling-of-2d-multidimensional-array-of-type-double-as-input-and-out%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

404 Error Contact Form 7 ajax form submitting

How to know if a Active Directory user can login interactively

TypeError: fit_transform() missing 1 required positional argument: 'X'