简体   繁体   English

从C#中的C ++ dll声明返回的函数是2点?

[英]Declare function which its return is 2-point from C++ dll in C#?

I have a function in C++ dll with its return is 2-point, as follows: 我在C ++ dll中有一个函数,其返回值为2点,如下所示:

#include "stdafx.h"
#include <vector>
using namespace std;

double** _stdcall f(int *n)
    {
        vector<double> t;
        vector<double> X;
        int i=0, j=0;
        do
        {
            t.push_back(3*i-4);
            X.push_back(2*j);

            i++;
            j++;
        }
        while (i<15&&j<90);
        *n=i;

        double** ret = new double*[2];
        for (i=0;i<2;i++)
            ret[i]=new double[*n];
        for (i=0;i<*n;i++)
        {
            ret[0][i]=t[i];
            ret[1][i]=X[i];
        }
        return ret;
    }

Now, I declare this function in C# as follows: 现在,我在C#中声明此函数,如下所示:

[DllImport("exDP.dll")]
public static extern IntPtr[2] f(ref int n_);

But there is an error for the syntax of this declaration. 但是此声明的语法有误。

I study as topic: How to get return array from function with global variable from C++ dll in C#? 我研究的主题是: 如何从C#中的C ++ dll中获取带有全局变量的函数的返回数组?

How to declare correctly with this function? 如何使用此函数正确声明? Thanks. 谢谢。

Edit: I fixed the error above, remove "2" (the size of array IntPtr) and it's: 编辑:我修复了上面的错误,删除“ 2”(数组IntPtr的大小),它是:

[DllImport("exDP.dll")]
public static extern IntPtr[] f(ref int n_);

Now, all of C# code as follows: 现在,所有C#代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;

namespace pABC
{
    public partial class frmABC : Form
    {

        [DllImport("exDP.dll")]
        public static extern IntPtr[] f(ref int n_);


        public frmABC()
        {
            InitializeComponent();
        }

        private void cmdOK_Click(object sender, EventArgs e)
        {
                int n = 0, i;


                IntPtr[] ret = new IntPtr[2];
                ret = f(ref n);

                double[] t = new double[n];
                Marshal.Copy(ret[0], t, 0, n);
                double[] X = new double[n];
                Marshal.Copy(ret[1], X, 0, n);

                MessageBox.Show("X[0]= " + X[0].ToString());
        }

    }
}

I compile OK. 我编译好了。 When I run it, an error occurs at the line: 当我运行它时,在此行出现错误:

ret = f(ref n);

That is: Cannot marshal 'return value': Invalid managed/unmanaged type combination. 即: 无法编组“返回值”:无效的托管/非托管类型组合。

How to fix it and get the results correctly. 如何修复它并正确获得结果。 Thanks. 谢谢。

You should do the same, as you did with one-dimension array. 与一维数组一样,您应该执行相同的操作。

In fact, C pointer ( * ) is just an integer (4-byte in 32-bit OS and 8-byte in 64-bit OS) that is allocated on the stack and points to a memory on a heap that contains your data. 实际上,C指针( * )只是一个整数(在32位OS中为4字节,在64位OS中为8字节),它分配在堆栈上并指向包含数据的堆上的内存。 C arrays are just sequences of data with elements located in memory one after another. C数组只是数据序列,其元素一个接一个地位于内存中。 For example, double* in your code points to an array of double s. 例如,代码中的double*指向double数组。

Now, when you create a multidimensional array (your double** , for example), you are actually creating an array of pointers to arrays. 现在,当您创建多维数组(例如,您的double** )时,实际上是在创建指向数组的指针的数组。 That means, the pointer (double*)* actually points to an array of double* s, and each of them points to an array of double s. 这意味着指针(double*)*实际上指向double* s的数组,并且每个指针都指向double s的数组。

Well, I guess you already know that :) 好吧,我想你已经知道了:)

Now, regarding interoperation with C#. 现在,关于与C#的互操作。 Your C# code in your case expects a pointer type, that is, IntPtr . 您的C#代码需要一个指针类型,即IntPtr To correctly interop this code, you should yet again return an IntPtr and use Marshal.Copy method, as you did before in your previous question. 要正确地互操作此代码,您应该再次返回IntPtr并使用Marshal.Copy方法,就像之前在上一个问题中所做的那样。

But now, after the first call of Marshal.Copy , you will get an array of pointers - that is, IntPtr . 但是现在,在第一次调用Marshal.Copy ,您将获得一个指针数组-即IntPtr For each of these pointers you should call Marshal.Copy yet again to get your double s array. 对于这些指针中的每一个,都应该调用Marshal.Copy ,再次Marshal.Copy以获得double数组。

The code would look some kind like this: 代码看起来像这样:

[DllImport("exDP.dll")]
// x and y are the dimensions of the array.
public static extern IntPtr f(ref int x, ref int y);

private void cmdOK_Click(object sender, EventArgs e)
{
    int x, y;

    IntPtr ret = f(ref x, ref y);

    IntPtr[] t = new IntPtr[x];
    Marshal.Copy(ret, t, 0, x);
    double[][] X = new double[x][y];
    for (int i = 0; i < x; i++)         
    {
        Marshal.Copy(t[i], X[i], 0, y);
    }

    //MessageBox.Show("X[0]= " + X[0].ToString());
}

If you will ever have a three-dimensional array ( double*** ), you will need to have one more loop, and so on. 如果您将拥有三维数组( double*** ),则将需要再创建一个循环,依此类推。

Now, regarding the memory leak issue. 现在,关于内存泄漏问题。 You can create your arrays in C# before passing them to the C++ code, as the others suggested. 您可以按照其他建议,在将数组传递给C ++代码之前,使用C#创建数组。 But you can also free the memory in C++ by simply exporting another function (let's call it clear ) and passing the original IntPtr there: 但是,您还可以通过简单地导出另一个函数(让我们将其称为clear )并将原始IntPtr传递到C ++中来释放C ++中的内存:

C++ : C ++

// x is the first dimension of the array
void __stdcall clear(double** arr, int x)
{
    for (int i = 0; i < x; i++)
    {
        // free all inner arrays
        delete[] arr[i];
    }
    delete[] arr;
}

C# : C#

[DllImport("exDP.dll")]
public static extern void clear(IntPtr arr, int x);

private void cmdOK_Click(object sender, EventArgs e)
{
    int x, y;
    IntPtr ret = f(ref x, ref y);
    ...
    for (int i = 0; i < x; i++)         
    {
        Marshal.Copy(t[i], X[i], 0, y);
    }
    clear(ret, x); // <-- this
}

The C Runtime is aware of the amount of memory it has previously allocated for each of these pointers, so you won't need to worry about the correctness of delete[] operation. C运行时知道它先前为每个指针分配的内存量 ,因此您不必担心delete[]操作的正确性。 But you must be sure to call this function right after you have Marshal.Copy ied your arrays, because if you will allocate the new arrays by another call to f() , this function will free the new ones, and the old ones will stay in memory. 但是您必须确保在拥有Marshal.Copy之后立即调用此函数。复制数组,因为如果通过再次调用f()分配新数组,则此函数将释放新数组,而旧数组将保留在记忆中。

I hope I have cleared some things out so you can now start coding your project :) 我希望我已经清除了一些东西,以便您现在就可以开始对项目进行编码了:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM