2015-01-15 32 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)如下:它是在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++ DL方法:

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++从C#DLL方法我收到以下错误

1 //尝试读取或写入受保护的内存。这通常表示在x64中运行C#代码时,其他内存已损坏。

2 //当在x86 Active/x86或AnyCPU平台中运行时,尝试加载格式不正确的程序。

问题:

1 //上述代码有什么问题?

2 //考虑到我的最终目标是在C++中创建一个2d动态数组,并在数组中读取/写入数据,例如上面的matrixheader.h文件中的double ** mat?其他方式来实现它?

+0

你是否意识到'Make'返回一个指针到堆栈?这是*非常差* –

+0

那么我们如何解决这个问题呢?你能否指点我一篇文章,进一步解释这个错误? –

+0

您也可以改为使用C++/CLI包装器,并在C#中使用更直接,更清晰的用法。 – crashmstr

回答

1

让我们容易的事第一:

试图在x86的主动/ x86或在AnyCPU平台捉迷藏时加载的程序与格式不正确。

这只是意味着你有一个平台不匹配。您要么尝试在x64 .NET运行时加载x86 C++ dll,要么相反。

以下错误是真正的问题:

尝试读取或写入受保护memory.This通常指示其他内存在运行x64的C#代码时已损坏。

这是可以预料的,因为你的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;调用不会释放每个分配的内存块。您在构造函数m + 1次中调用new,这意味着您必须在析构函数中调用delete[]m + 1次,每次调用new。您可能应该保留mn作为您班级中的字段(至少需要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长度的'std :: vector'然后索引。单独分配矩阵的每一行实际上要昂贵得多,并且使索引变慢。 – Mgetz

+0

@Mgetz绝对,我会建议同样的事情,但你打我; –

+0

@Mgetz和卢卡斯感谢家伙,我会采纳你的建议,并实施them.I知道我可以做这个使用矢量类作为Mgetz建议并展平阵列,但我想知道哪种方法更快,因此上述实现 –