在C#和C ++之间作为输入和输出的double类型的二维多维数组的pinvoke编组 [英] pinvoke marshalling of 2d multidimensional array of type double as input and output between c# and c++

查看:83
本文介绍了在C#和C ++之间作为输入和输出的double类型的二维多维数组的pinvoke编组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试解决以下具有双重问题类型的二维多维数组的c#和c ++ pinvoke编组问题.

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++ .

我已经审查了将C#锯齿状数组编组为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.

我的问题是,到目前为止,如果我走的路正确,我应该如何解开从已启用DllImport的调用成功传递回c#IntPtr outputArrayPtr变量的c ++ *outputArray = new double[*outputArrayRows, *outputArrayCols];var outputArray = new double[outputArrayRows, outputArrayCols];变量中需要进行操作.

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.

问题=是否有关于for循环是否正确的下一步以及我在其中使用哪种提取语法的任何见解?

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

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;
}

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);
}


基于注释,我更新了标题以表示此问题与尝试传递和接收2d [/多维]数组而不是锯齿状数组有关.也就是说,在我的测试中显而易见的是,vs17 c ++ Windows桌面dll项目环境仅执行锯齿状数组[例如c ++ DllExport double** SomeFunction(double** inputArray, . . .double** returnArray = new double*[numberOfRows]和c#double[][] dogLegValues = new double[numberOfRows][/* numberOfCols not specified */];].我在下面添加了我可以使用的c#pinvoke DllImport和c ++函数签名以及一些有趣的编组代码,这些代码用于准备2d数组作为锯齿形数组传递并处理返回的锯齿形数组最终将其转换为呼叫者期望的二维数组是否对其他人有帮助.

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语句和注释捕获发现结果

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 ++函数签名

#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

将2d数组展平为1d数组的c#封送处理代码

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);

用于2d数组的c#封送处理代码

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 ++锯齿状数组的初始化和赋值示例

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;

推荐答案

此代码:

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

不执行您认为的操作. (我缩短了变量名,因为它们只是使事情复杂化了.)

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

C ++从C继承了多维数组的不足.它所具有的是数组数组或指向数组的指针数组.在这两种情况下,都将它们索引为array[firstIndex][secondIndex].您不能new这样的指针数组,而必须编写如下内容:

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];
}

您实际调用的是C ++逗号运算符,该运算符求值并丢弃第一个操作数,然后求值第​​二个操作数.提高您的编译器警告;一个好的编译器会警告说,第一个操作数没有副作用.

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.

这篇关于在C#和C ++之间作为输入和输出的double类型的二维多维数组的pinvoke编组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆