2

我有一个 C++ 类,它有一个 header(matrixheader.h),这样:

#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++中创建一个二维动态数组并在数组中读取/写入数据,例如C#上面matrixheader.h文件中的一个double**mat?还有其他方法可以实现吗?它 ?

4

1 回答 1

1

让我们先搞定简单的事情:

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

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

以下错误是真正的问题:

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

这是意料之中的,因为您的Make函数在堆栈上创建了一个对象,然后返回一个指向它的指针。当你读回这个对象时,堆栈上的内容已经改变(堆栈正在被重用),并且mat指针指向其他地方,很可能指向未分配的内存。

参阅这个答案,我将在其中更深入地了解这个问题(它是 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. 您可能应该在您的班级中保留mn作为字段(至少m必须知道您必须进行多少次调用delete[])。

更好的解决方案是使用单个数组而不是锯齿状数组。您i, j将该数组中的索引计算为i * m + j。您也可以使用 astd::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; }
    }
}
于 2015-01-15T12:56:33.517 回答