2015-01-15 8 views
2

У меня есть C++ класс, который имеет заголовок (matrixheader.h) таким образом, что:Чтение Запись C++ Динамические массивы в C# (InteropServices)

#pragma once 

class M 
{ 
    public: 
     M(int m,int n); 
     void MSet(int m,int n,double d); 
     double MGet(int m,int n); 
     ~M(); 
    private: 
     double** mat; 
}; 

класса определяется следующим образом в (matrixbody.cpp): It встроен в платформу Win32.

#pragma once 
#include "matrixhead.h" 

M::M(int m,int n) 
{ 
    mat = new double*[m]; 
    for (int i = 0; i < m; i++) 
    { 
    mat[i] = new double[n]; 
    } 
} 

void M::MSet(int m,int n,double d) 
{ 
    mat[m][n] = d; 
} 

double M::MGet(int m,int n) 
{ 
    double d = mat[m][n]; 
    return d; 
} 

M::~M() 
{ 
    delete[] mat; 
} 

Я сделал обертку для класса как так (matrixwrapper.cpp): Оболочка также встроена в платформу Win32.

#include "matrixhead.h" 
#include "matrixbody.cpp" 

extern "C" __declspec(dllexport) void* Make(int m,int n) 
{ 
    M o(m,n); 
    return &o; 
} 

extern "C" __declspec(dllexport) void setData(void* mp,int m,int n,double d) 
{ 
    M* ap = (M*)mp; 
    M a = *ap; 
    a.MSet(m,n,d); 
} 

extern "C" __declspec(dllexport) double getData(void* mp,int m,int n) 
{ 
    M* bp = (M*)mp; 
    M b = *bp; 
    double d = b.MGet(m,n); 
    return d; 
} 

импортировать класс в C# и попытаться вызвать C++ методы Д.Л. из C#:

using System; 
using System.Runtime.InteropServices; 


namespace wrappertest 
{ 
class Program 
    { 
    [DllImport("matrixwrapper.dll")] 
    unsafe public static extern void* Make(int m,int n); 

    [DllImport("matrixwrapper.dll")] 
    unsafe public static extern void setData(void* mp,int m, int n,double d); 

    [DllImport("matrixwrapper.dll")] 
    unsafe public static extern double getData(void* mp,int m, int n); 

    static unsafe void Main(string[] args) 
    { 
     void* p = Make(10, 10); 
     setData(p,10,1,10); 
     Console.WriteLine(getData(p,10,1)); 
    } 
    } 
} 

Но когда я пытаюсь запустить C++ методы DLL из C# я получаю следующее сообщение об ошибке

1 // Попытка чтения или записи защищенной памяти. Это часто свидетельствует о том, что другая память повреждена при запуске кода C# в x64.

2 // Была предпринята попытка загрузить программу с неправильным форматом при запуске в x86 Active/x86 или на платформе AnyCPU.

Вопросы:

1 // Что плохого в приведенном выше коде?

2 // Учитывая, что моя конечная цель состоит в том, чтобы сделать 2d динамический массив на C++ и читать/записывать данные в массиве, такие как один двойной ** мат в файле matrixheader.h выше из C#? Есть ли какие-либо другой способ его реализовать?

+0

Вы понимаете, что 'Make' возвращает указатель на стек? Это очень * плохо ... –

+0

Итак, как мы это исправим? Можете ли вы указать мне статью, в которой объясняется эта ошибка? –

+0

Вы также можете сделать оболочку C++/CLI и иметь более прямое и чистое использование в C#. – crashmstr

ответ

1

Давайте получить легкий вещь первый:

Была сделана попытка загрузить программу с неправильным форматом, когда в стереосистеме и обновите x86/x86 активной или AnyCPU платформе.

Это просто означает, что у вас есть несоответствие платформы. Вы либо пытаетесь загрузить dll x86 C++ в среду выполнения x64 .NET, либо наоборот.

Следующая ошибка является реальной проблемой:

Попытка чтения или записи в защищенную memory.This часто признак того, что другая память повреждена при запуске C# код в x64.

Этого следует ожидать, потому что ваша функция Make создает объект в стеке, а затем возвращает указатель на него. Когда вы прочитаете этот объект, содержимое в стеке изменилось (стек будет повторно использован), а указатель mat укажет в другом месте, скорее всего, на нераспределенную память.

Пожалуйста, see this answer, где я подробно расскажу об этой проблеме (это C#, но это та же проблема).

Чтобы решить вашу проблему, вам необходимо выделить некоторую динамическую память.Вы можете попробовать:

extern "C" __declspec(dllexport) void* Make(int m,int n) 
{ 
    M* o = new M(m,n); 
    return o; 
} 

И, конечно же, вам придется создать еще один способ, чтобы выполнить соответствующий delete, если вы не хотите, чтобы утечка памяти.

Кроме того, как отмечает Mgetz в комментариях, у вас есть утечка памяти в самом классе M. Вызов delete[] mat; в деструкторе не освобождает каждый выделенный фрагмент памяти. Вы вызываете new в конструкторе m + 1 раз, это означает, что вы должны называть delete[]m + 1 раз также в деструкторе, один раз для каждого new. Вероятно, вы должны сохранить m и n в качестве полей вашего класса (не менее m обязательно знать, сколько звонков требуется delete[]).

Лучшим решением было бы использовать только один массив вместо зубчатых массивов. Вы вычисляете индекс i, j в этом массиве как i * m + j. Вы также можете использовать std::vector или просто сделать это в C# вообще:

public class M 
{ 
    private double[] _items; 
    private int _m; 
    private int _n; 

    public M(int m, int n) 
    { 
     _items = new double[m * n]; 
     _m = m; 
     _n = n; 
    } 

    public this[int i, int j] 
    { 
     // Here, you should perform a bounds check on i and j against _m and _n 
     get { return _items[i * _m + j]; } 
     set { _items[i * _m + j] = value; } 
    } 
} 
+1

ему даже не нужен C++, он может просто создать все на C#, и он, вероятно, будет быстрее, чем стоимость взаимодействия. Если по какой-то причине ему нужен C++, он должен, вероятно, просто использовать m * n length 'std :: vector', а затем индексировать. Разделение каждой строки матрицы по отдельности на самом деле намного дороже и делает индексацию в ней медленнее. – Mgetz

+0

@Mgetz абсолютно, я собирался предложить то же самое, но вы избили меня;) –

+0

@Mgetz и Lucas спасибо, ребята, я приму ваши предложения и реализую их. Я знал, что могу сделать это, используя векторный класс как Mgetz предложил и сплющил массив, но я хотел выяснить, какой метод был быстрее, тем выше реализация –