У меня есть следующее c # и c ++ pinvoke marshalling 2d многомерного массива типа double вещество, которое я пытаюсь решить.
Я просмотрел следующий запрос, чтобы получить то, что у меня есть в настоящее время P / Invoke с массивами данных с двойной сортировкой между C # и C ++ .
Я просмотрел Marshalling C # Jagged Array для C ++ , который имеет очень хорошее совпадение сценариев, но не ясно, как перейти от ответа ко всем аспектам реализации.
Моя проблема, думаю, если я нахожусь наПравильный путь до сих пор, это то, как я раскручиваю c ++ *outputArray = new double[*outputArrayRows, *outputArrayCols];
, который успешно передается обратно из вызова с поддержкой DllImport в переменную c # IntPtr outputArrayPtr
в переменную var outputArray = new double[outputArrayRows, outputArrayCols];
, необходимую для продолжения.
Вопрос= Любое понимание того, является ли цикл for правильным следующим шагом и какой синтаксис я использую внутри него?
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 [/ многомерный] массив nне зубчатый массив.Тем не менее, в моих тестах стало очевидным, что среда проекта dll vs17 c ++ windows desktop делает только неровные массивы [например, 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-массива для передачи в виде зубчатого массива и для обработки возвращенного зубчатого массива, в конечном итоге преобразующего его в2d массив, который ожидал вызывающий объект, если это помогает другим.
c # Оператор DllImport и комментарии, фиксирующие результаты
[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
c # код маршалинга для 2dмассив сведен в массив 1d
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 # код маршалинга для массива 2d
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 ++ примеры инициализации и назначения зубчатых массивов
// 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;