如何将 C# 中的双数组正确发送到 C dll?

问题描述

我有一个从 C# 应用程序调用的 C DLL。我能够成功地从 DLL 接收结构,但我在将数据发送到 DLL 中时遇到了发送相同的结构的问题。这是我所拥有的:

C# 应用程序结构:

public class MatrixClass
{

    private double[,] cells;

    [StructLayout(LayoutKind.Sequential)]
    public struct Matrix
    {
        public IntPtr cells;
        public int cellRows;
        public int cellColumns;   
    }
}

C DLL 结构:

    typedef struct
    {
        double **cells;
        int cellRows;
        int cellColumns;
    } Matrix;

这是我的 C DLL 中的示例函数,我需要从中接收结构:

void GetMatrixCell(Matrix *MatrixIn,int nRow,int nColumn,double *value)
{
    *value = 0.0;
    int nColIndex = nColumn - 1;
    int nRowIndex = nRow - 1;
    *value = MatrixIn->cells[nRowIndex][nColIndex];
}

我怎么称呼它:

[DllImport(matrixDLL,CallingConvention = CallingConvention.Cdecl)]
static extern int GetMatrixCell(Matrix mIn,int 
nColumn,ref double value);

public double GetCell(int Row,int Column)
{
    double result = 0.0;
    int stride = Rows * sizeof(double);
    IntPtr p = Marshal.AllocHGlobal(Rows * stride);
    Matrix mIn = MatrixtoDLL(this,Rows,Columns,p,stride); //this = MatrixClass
    GetMatrixCell(mIn,Row,Column,ref result);
    Marshal.FreeHGlobal(p);
    return result;
}

MatrixtoDll 函数来自上面:

    public static Matrix MatrixtoDLL(MatrixClass input,int rows,int columns,IntPtr p,int stride)
    {
        Matrix toReturn = new Matrix ();
        toReturn.cellColumns = columns;
        toReturn.cellRows = rows;

        for (int i = 0; i < columns; i++) //rows
        {
            double[] temp = new double[rows];

            for (int x = 0; x < rows; x++)
                temp[x] = input.cells[i,x]; //[columns,rows]

            Marshal.copy(temp,p + stride * i,rows);
        }

        toReturn.cells = p;

        return toReturn;
    }

我在想我的 MatrixtoDLL 是我需要重新思考的地方吗?或者我需要以另一种方式发送数据。就像我上面提到的,我可以成功地将数据(单元格)作为 IntPtr 接收并将其转换为二维数组,但程序不喜欢他们尝试发送结构的方式。提前致谢!

编辑:这是我得到的未处理异常: mscorlib.dll 中发生类型为“System.Reflection.TargetInvocationException”的未处理异常 附加信息:调用的目标已抛出异常。

解决方法

您的 P/Invoke 函数定义不正确。应该是

[DllImport(matrixDLL,CallingConvention = CallingConvention.Cdecl)]
static extern void GetMatrixCell(in Matrix mIn,int nRow,int nColumn,ref double value);

此外,Matrix.cells 应该是一个指向一维数组的指针数组的指针,换句话说,一个锯齿状数组。

我们可以为此使用GCHandle,这也避免了不必要的复制

    private double[][] cells;

    [StructLayout(LayoutKind.Sequential)]
    public struct Matrix
    {
        public IntPtr[] cells;
        public int cellRows;
        public int cellColumns;   
    }

public double GetCell(int Row,int Column)
{
    double result = 0.0;
    Matrix mIn = new Matrix
    {
        cellColumns = Columns,cellRows = Rows,};

    var handles = new GCHandle[this.cells.Length];

    try
    {
        for (var i = 0; i < this.cells.Length; i++)
            handles[i] = GCHandle.Alloc(this.cells[i],GCHandleType.Pinned);

        mIn.cells = handles.Select(h => h.AddrOfPinnedObject()).ToArray();
        GetMatrixCell(mIn,Row,Column,ref result);
    }
    finally
    {
        foreach (var h in handles)
            if(h.IsAlllocated)
                h.Free();
    }
    return result;
}

我必须说,我不清楚 Matrix.cellColumnscellRows 的重点。 C 函数似乎没有使用它。