【问题标题】:Passing Int* and double* from C++ to C#将 Int* 和 double* 从 C++ 传递到 C#
【发布时间】:2021-09-15 08:39:36
【问题描述】:

我正在尝试从 C++ 插件中检索两个要在 C# 中使用的数组。

代码如下:

C++

函数“funcplugin”有一个 void 返回类型,我希望返回两个长度为 16 和 24 的单维数组。

extern "C" __attribute__ ((visibility ("default")))
void funcplugin(int* corners, double* points)
{
   corners = new int[16];
   points = new double[24];
   //code to compute and populate array values
}

C#

private IntPtr corners;
private IntPtr points;
private int size_corners =16;
private int size_points =24;

[DllImport ("native")]
private static extern void funcplugin(IntPtr corners, IntPtr points);

void call
{
   int[] first = new int[16];
   double[] second = new double[24];
   corners = Marshal.AllocHGlobal(size_corners);
   points = Marshal.AllocHGlobal(size_points);
   
   funcplugin(corners, points);

   Marshal.Copy(corners, first, 0, size_corners);
   Marshal.Copy(points, second, 0, size_points);

   for (int i=0; i<16; i++)
        {
            Debug.Log("corners: " + first[i]);
        }
   for (int j=0; j<24; j++)
        {
            Debug.Log("points " + second[j]);
        }
   
   //code to use the array
   
   Marshal.FreeHGlobal(corners);
   Marshal.FreeHGlobal(points);

}

Debug.Log 语句为数组提供垃圾值。我确定我在检索值时做错了。 任何帮助,将不胜感激。 谢谢。

【问题讨论】:

  • 您需要将大小乘以Marshal.SizeOf&lt;int&gt;()Marshal.SizeOf&lt;double&gt;(),也应该将它们全部放在tryFreeHGlobal 中的finally 中。为什么不直接使用标准编组:private static extern void funcplugin(ref int[] corners, ref double[] points);

标签: c# c++


【解决方案1】:

在 C++ 代码中,指针按值传递,因此无论函数设置它们指向什么地址,调用者都无法接收这些地址。为此,您需要按引用按指针传递指针,例如:

extern "C" __attribute__ ((visibility ("default")))
void funcplugin(int* &corners, double* &points)
{
   corners = new int[16];
   points = new double[24];
   //code to compute and populate array values
}

或者:

extern "C" __attribute__ ((visibility ("default")))
void funcplugin(int** corners, double** points)
{
   *corners = new int[16];
   *points = new double[24];
   //code to compute and populate array values
}

此外,如果函数使用调用者无法直接访问的任何内存管理器(即,通过new[])分配数组,您将需要导出一个函数以使用相同的内存管理器释放数组(即,通过delete[]) 在调用者完成使用它们之后,例如:

例如:

extern "C" __attribute__ ((visibility ("default")))
void funcpluginfree(int* corners, double* points)
{
   delete[] corners;
   delete[] points;
}

然后在 C# 端,您可以这样做:

private int num_corners = 16;
private int num_points = 24;

[DllImport ("native")]
private static extern void funcplugin(out IntPtr corners, out IntPtr points);

[DllImport ("native")]
private static extern void funcpluginfree(IntPtr corners, IntPtr points);

void call
{
   int[] first = new int[num_corners];
   double[] second = new double[num_points];

   IntPtr corners;
   IntPtr points;
   
   funcplugin(out corners, out points);

   Marshal.Copy(corners, first, 0, num_corners);
   Marshal.Copy(points, second, 0, num_points);

   funcpluginfree(corners, points);

   for (int i = 0; i < num_corners; i++)
   {
       Debug.Log("corners: " + first[i]);
   }
   for (int j = 0; j < num_points; j++)
   {
       Debug.Log("points " + second[j]);
   }
}

现在,话虽这么说......

您的原始 C# 代码正在分配内存(但未分配足够的内存,顺便说一句)以传递给 C++ 代码以填充数据。在这种情况下,C++ 代码没有理由也分配自己的内存。所以,你可以在funcplugin()里面去掉new[],这样就不需要导出一个函数来调用delete[],例如:

C++

extern "C" __attribute__ ((visibility ("default")))
void funcplugin(int* corners, double* points)
{
   //code to compute and populate array values
}

C#

private int num_corners = 16;
private int num_points = 24;

[DllImport ("native")]
private static extern void funcplugin(IntPtr corners, IntPtr points);

void call
{
   int[] first = new int[num_corners];
   double[] second = new double[num_points];

   IntPtr corners = Marshal.AllocHGlobal(num_corners * sizeof(int));
   IntPtr points = Marshal.AllocHGlobal(num_points * sizeof(double));
   
   funcplugin(corners, points);

   Marshal.Copy(corners, first, 0, num_corners);
   Marshal.Copy(points, second, 0, num_points);

   Marshal.FreeHGlobal(corners);
   Marshal.FreeHGlobal(points);

   for (int i = 0; i < num_corners; i++)
   {
       Debug.Log("corners: " + first[i]);
   }
   for (int j = 0; j < num_points; j++)
   {
       Debug.Log("points " + second[j]);
   }
}

或者,正如cmets中提到的@Charlieface,您可以使用C#的原生array marshaling(也可以是see this)而不是手动分配内存,例如:

private int num_corners = 16;
private int num_points = 24;

[DllImport ("native")]
private static extern void funcplugin([In, Out] int[] corners, [In, Out] double[] points);

void call
{
   int[] first = new int[num_corners];
   double[] second = new double[num_points];
   
   funcplugin(first, second);

   for (int i = 0; i < num_corners; i++)
   {
       Debug.Log("corners: " + first[i]);
   }
   for (int j = 0; j < num_points; j++)
   {
       Debug.Log("points " + second[j]);
   }
}

【讨论】:

  • 完美!谢谢@Remy 的详细解释。非常感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-19
  • 2019-01-31
  • 1970-01-01
  • 1970-01-01
  • 2012-02-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多