2015-01-15 35 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建議並展平陣列,但我想知道哪種方法更快,因此上述實現 –