IntPtr в трехмерный массив

Я использую dll С++ в своем проекте С#. Использование pInvoke (вызов платформы).

Мой код C++ возвращает значение типа double***, а функция C# возвращает значение IntPtr.

У меня размеры матрицы с обеих сторон, просто они непостоянны.

На стороне С# у меня есть:

[DllImport("mylib.dll")]
public static extern IntPtr GetMatrix(int width, int height, int depth);

А на стороне С++ у меня есть:

extern "C" {
    __declspec(dllexport) double*** GetMatrix(int width, int height, int depth) {
        // do stuff
        return matrix;
    }
}

Теперь мне нужно преобразовать этот IntPtr в double[,,], но я понятия не имею, как это сделать.

В основном мне нужно противоположное ответу этот вопрос.


person Pedro Henrique    schedule 18.07.2015    source источник
comment
Вам нужно знать больше. Недостаточно знать тип. Также необходимо знать размеры.   -  person David Heffernan    schedule 19.07.2015
comment
Я знаю размеры в обе стороны, просто они непостоянны. Я отредактировал вопрос...   -  person Pedro Henrique    schedule 20.07.2015
comment
Я решил это, вместо того, чтобы возвращать всю матрицу, я просто возвращаю один двойной. Так что да, я вызываю DLL несколько раз. По крайней мере, значения приходят правильные. Но мой вопрос до сих пор не имеет решения...   -  person Pedro Henrique    schedule 20.07.2015
comment
Чего не хватает в вопросе?   -  person Pedro Henrique    schedule 21.07.2015
comment
Описание как получить размеры для начала   -  person David Heffernan    schedule 21.07.2015
comment
Я отредактировал вопрос. Пользователь предоставляет размеры функции GetMatrix на стороне C#. Что еще тебе нужно?   -  person Pedro Henrique    schedule 23.07.2015


Ответы (1)


Я никогда не пробовал это с такими типами массивов, но это первое решение может работать, и я уверен, что эта статья MSDN:

(Обратите внимание, что это работает только в том случае, если вы можете изменить определение функции на стороне C#). В вашей функции C# измените параметр IntPtr на параметр double[,,] и добавьте к нему этот атрибут MarshalAs, аналогично тому, как это делается в приведенной выше статье MSDN.

[DllImport("FooBar.dll")]
extern void Foo([MarshalAs(UnmanagedType.LPArray, SizeConst=**YOUR ARRAY SIZE HERE**)] double[,,] bar);

Это также предполагает, что у вас есть массив постоянного размера, и я не уверен, как это точно будет работать с трехмерным массивом, но если вы хотите подробнее изучить этот тип решения, вот полная документация для MarshalAsAttribute.


Другое решение очень похоже на противоположное этому другому вопросу. Вы должны инициализировать свой double[,,] до нужного вам размера, а затем начать заполнять строки с помощью Marshal.Copy (эта перегрузка). Вы также можете маршалировать по одному двойнику с той же функцией. Что-то вроде этого:

double[,,] arr = new double[25, 25, 200];
int il = arr.GetLength(0), jl = arr.GetLength(1), kl = arr.GetLength(2);

IntPtr values = DllFoo();

for (int i = 0; i < il; i++)
{
    for (int j = 0; j < jl; j++)
    {
        for (int k = 0; k < kl; k++)
        {
            double[] temp = new double[1];
            arr[i,j,k] = Marshal.Copy(values, temp, 1, k + j * jl + k * jl * kl);
        }
    }    
}

Вы определенно можете выжать гораздо больше производительности из этого фрагмента, но он все равно должен работать.

person Robert Rouhani    schedule 18.07.2015
comment
Первое решение мне не подойдет, так как размер матрицы не будет постоянным. Второй выдал исключение (Запрошенный диапазон выходит за пределы массива) в строке Marshal.Copy. И я уверен, что передаю вашей функции правильные значения размера. - person Pedro Henrique; 19.07.2015
comment
Кроме того, мне пришлось немного изменить его, поскольку Marshal.Copy возвращает void, я предположил, что значение, которое должно быть сохранено, равно temp[0], поэтому arr[i,j,k] = temp[0]. Это правильно? - person Pedro Henrique; 19.07.2015
comment
О, точно, так и должно быть. Написал весь свой код прямо на SO, поэтому он никогда не компилировался или что-то в этом роде: P - person Robert Rouhani; 19.07.2015