我正在尝试解决以下具有双重问题类型的二维多维数组的c#和c ++ pinvoke编组问题。
我已经查看了以下匹配内容,以了解我目前P/Invoke with arrays of double - marshalling data between C# and C++的情况。
我已经查看了Marshalling C# Jagged Array to C++,它具有非常好的场景匹配,但是尚不清楚如何从答案到实现的所有方面。
我的问题是,到目前为止,如果我走的路正确,我要解开如何成功地将c ++ *outputArray = new double[*outputArrayRows, *outputArrayCols];
从启用DllImport的调用传递回c#IntPtr outputArrayPtr
变量到{我需要{1}}变量才能继续。
问题=是否有关于for循环是否正确的下一步以及我在其中使用哪种提取语法的任何见解?
c ++方面
var outputArray = new double[outputArrayRows, outputArrayCols];
c#方面
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;
}
基于注释,我更新了标题以表示此问题与尝试传递和接收2d [/多维]数组而不是锯齿状数组有关。就是说,在我的测试中显而易见的是,vs17 c ++ Windows桌面dll项目环境仅执行锯齿状数组[例如c ++ [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);
}
和DllExport double** SomeFunction(double** inputArray, . . .
和c#double** returnArray = new double*[numberOfRows]
]。在下面,我添加了我能够使用的c#pinvoke DllImport和c ++函数签名以及一些有趣的编组代码,这些代码用于准备2d数组作为锯齿形数组传递并处理返回的锯齿形数组最终将其转换为呼叫者期望的二维数组是否对其他人有帮助。
c#DllImport语句和注释捕获发现结果
double[][] dogLegValues = new double[numberOfRows][/* numberOfCols not specified */];
c ++函数签名
[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
将2d数组展平为1d数组的c#封送处理代码
#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#二维数组的封送处理代码
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 ++锯齿状数组的初始化和赋值示例
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();
答案 0 :(得分:1)
此代码:
*values = new double[*valuesOuterLen, *valuesInnerLen];
不执行您认为的操作。 (我缩短了变量名,因为它们使事情复杂了。)
C ++缺乏C的多维数组。它具有的是数组数组或指向数组的指针数组。在这两种情况下,您都将它们索引为array[firstIndex][secondIndex]
。您不能new
这样的指针数组,而必须编写如下内容:
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];
}
您实际调用的是C ++逗号运算符,该运算符求值并丢弃第一个操作数,然后求值第二个操作数。提高您的编译器警告;一个好的编译器会警告第一个操作数没有副作用。