pinvoke marshalling of 2d multidimensional array of type double as input and output between c# and c++
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
add a comment |
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
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
add a comment |
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
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
c# c++ interop pinvoke marshalling
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
add a comment |
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
add a comment |
1 Answer
1
active
oldest
votes
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.
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
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
add a comment |
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.
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
add a comment |
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.
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.
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
add a comment |
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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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