C#将引用类型对象数组传递给C++; 我通过C代码> C++ >结构>布局(LayOuto.序列式)< /COD> ,将C类型的引用对象成功地传递给C++。

C#将引用类型对象数组传递给C++; 我通过C代码> C++ >结构>布局(LayOuto.序列式)< /COD> ,将C类型的引用对象成功地传递给C++。,c#,c++,arrays,pinvoke,marshalling,C#,C++,Arrays,Pinvoke,Marshalling,C#中的班级: C++中的等价物: struct Point { public: double x; double y; }; C++中的函数体: extern "C" __declspec(dllexport) void Translate(Point* pt, double dx, double dy) { pt->x += dx; pt->y += dy; } 和C#中的等价物: 到目前为止,我还没有遇到任何问题,当我在接下来的两个函数之间传

C#中的班级:

C++中的等价物:

struct Point
{
public:
    double x;
    double y;
};
C++中的函数体:

extern "C" __declspec(dllexport) void Translate(Point* pt, double dx, double dy)
{
    pt->x += dx;
    pt->y += dy;
}
和C#中的等价物:

到目前为止,我还没有遇到任何问题,当我在接下来的两个函数之间传递这些点的数组时,问题就出现了:

extern "C" __declspec(dllexport) double GetArea(Point** vertices,int count, bool isClosed)
    {
        double area = 0;
        if (!isClosed || count < 3) return 0;
        int j;
        for (int i = 0; i < count; i++)
        {
            // j = i+1 except when i = count then j = 0
            j = (i + 1) % count;

            area += vertices[i]->x * vertices[j]->y;
            area -= vertices[i]->y * vertices[j]->x;
        }
        area /= 2;
        return fabs(area);
    }
是否有特定的方法封送此数组?但是保持Point类的现状

调用方法如下所示:

public static double GetArea()
        {
            Point[] vertices = { new Point(100, 100), new Point(0, 0), new Point(0, 100) };
            bool isClosed = true;
            return NativeMethods.GetArea(vertices, vertices.Length, isClosed); //this is the imported C++ method
}
unsafe double CallGetAreaPointers(Point[] vertices, bool isClosed)
{
  var ipArray = Marshal.AllocHGlobal(vertices.Length * sizeof(Point));
  var ipItems = new Stack<IntPtr>();

  try
  {
    Point** pArray = (Point**)ipArray.ToPointer();

    for (var i = 0; i < vertices.Length; i++)
    {
      var ipItem = Marshal.AllocHGlobal(sizeof(Point));
      ipItems.Push(ipItem);

      Marshal.StructureToPtr(vertices[i], ipItem, false);

      pArray[i] = (Point*)ipItem.ToPointer();
    }

    GetArea(pArray, vertices.Length, isClosed);
  }
  finally
  {
    IntPtr ipItem;
    while ((ipItem = ipItems.Pop()) != IntPtr.Zero) Marshal.FreeHGlobal(ipItem);

    Marshal.FreeHGlobal(ipArray);
  }
}

您根本不应该进行类的互映射(明确支持的类除外,例如
string
、数组…)。NET的本地互操作是针对C样式函数和结构而不是C++设计的。因此,要么将本机代码更改为C样式,要么使用类似于C++ + CLI的方法来提供C++对象和函数的托管接口。 处理互操作的最简单方法是使用封送拆收器,并将
Point
更改为值类型,而不是引用类型:

struct Point
{
  public double x;
  public double y;
}

[DllImport("myDll.dll", CallingConvention = CallingConvention.Cdecl)]
public extern unsafe static double GetArea(Point[] vertices, int count, bool isClosed);
这当然需要
Point
成为一个可轻击的类型-仅仅将它变成
struct
而不是
class
就足够了。当
顶点
是指向一个值数组的指针时,您只需这样做。同样的方法,你需要使它成为C++侧的值,而不是指针(函数的签名将是代码> GETFAZE(点*顶点,…)< /C> >,而不是使用指针的指针)。 手动编组数组有两个主要选项-不安全代码和
IntPtr
。如果需要封送更复杂的内容,这可能很有用

不安全签名非常简单:

[DllImport("myDll.dll", CallingConvention = CallingConvention.Cdecl)]
public extern unsafe static double GetArea(Point** vertices, int count, bool isClosed);
创建和传递数组的方法与C中的方法相同——我不能提供任何具体的方法,因为这完全取决于谁拥有什么内存,以及内存是如何构造的。您可以轻松使用现有的.NET阵列,只需固定它们:

public static unsafe double CallGetArea(Point[] vertices, bool isClosed)
{
  if (vertices.Length == 0) throw new ArgumentException("Vertices empty.", "vertices");

  fixed (Point* pVertices = &vertices[0])
  {
    return GetArea(pVertices, vertices.Length);
  }
}
这假设您只想传递一个
值数组(签名将具有
点*
)。您不能像这样传递指向数组的指针-
pVertices
是只读的。同样,这只在
可空袭时有效

IntPtr
签名丢弃类型信息:

[DllImport("myDll.dll", CallingConvention = CallingConvention.Cdecl)]
public extern unsafe static double GetArea(IntPtr vertices, int count, bool isClosed);
同样,调用类似于C,您还需要执行许多恼人的marshaller调用,用于读写,而不是使用类似C的语言来完成

如果你真的想坚持你的想法,制作一个指向
点的指针数组
,事情会变得复杂一些。编组程序不支持指针数组,因此必须手动进行编组。当然,这涉及到大量不必要的复制和分配,因此如果您出于性能原因而尝试这样做,请不要这样做。基本思路如下:

public static double GetArea()
        {
            Point[] vertices = { new Point(100, 100), new Point(0, 0), new Point(0, 100) };
            bool isClosed = true;
            return NativeMethods.GetArea(vertices, vertices.Length, isClosed); //this is the imported C++ method
}
unsafe double CallGetAreaPointers(Point[] vertices, bool isClosed)
{
  var ipArray = Marshal.AllocHGlobal(vertices.Length * sizeof(Point));
  var ipItems = new Stack<IntPtr>();

  try
  {
    Point** pArray = (Point**)ipArray.ToPointer();

    for (var i = 0; i < vertices.Length; i++)
    {
      var ipItem = Marshal.AllocHGlobal(sizeof(Point));
      ipItems.Push(ipItem);

      Marshal.StructureToPtr(vertices[i], ipItem, false);

      pArray[i] = (Point*)ipItem.ToPointer();
    }

    GetArea(pArray, vertices.Length, isClosed);
  }
  finally
  {
    IntPtr ipItem;
    while ((ipItem = ipItems.Pop()) != IntPtr.Zero) Marshal.FreeHGlobal(ipItem);

    Marshal.FreeHGlobal(ipArray);
  }
}
不安全的双CallGetAreaPointers(点[]顶点,布尔值关闭)
{
var ipArray=Marshal.AllocHGlobal(顶点.Length*sizeof(点));
var ipItems=新堆栈();
尝试
{
点**阵列=(点**)ipArray.ToPointer();
对于(var i=0;i
(免责声明:这只是一个简单的代码;如果你决定这样做,请确保你做得正确,这只是一个一般的想法)


注意,为了简单起见,我仍然使用<代码>点<代码>作为<代码>结构> <代码>,它允许您在C++侧使用指针。如果您想完全使用C#中的引用类型,最简单的方法是创建一个

PointStruct
类型(如果您愿意,则为private),并将类字段复制到该结构中。当然,在这两种情况下,分别分配每个
实例没有什么意义-一个简单的值数组将以完全相同的方式工作,同时是一个连续的内存位,使用和清理更简单、更便宜。

将C#
设置为可blittable值类型要容易得多。因此
GetArea
有一个输出参数,它是
的数组?函数声明还不够。谁拥有记忆?它是如何构造的?
顶点
是指针数组还是指向值数组的指针?GetArea只返回一个双精度值,它不会改变点数组。所有对象都属于C#代码。顶点只是C#public Point[]顶点{get;private set;}中的一个点数组,那么为什么要传递指向
点的指针呢?为什么它不只是点*
?你是否也在编码C/C++方面?因为C引用类型总是通过引用C++传递的。我两边都在编码是的。让我用C++中的函数BoeDes更新一个结构,一个类几乎是相同的。第一段很混乱。真正的问题不是结构与类,而是引用与值。您提供的代码示例没有用处,因为数组是一个输入。那个ref是错误的,接下来的一切都是错误的。但本质是好的。使用值更好。只是细节有问题。没有。C没有引用。C++有引用,但它们是另外的东西。在C#中,结构与类是值与引用。同样的事情。在C++中,结构和类仅在默认情况下是成员私有的,类是默认的公共的。
unsafe double CallGetAreaPointers(Point[] vertices, bool isClosed)
{
  var ipArray = Marshal.AllocHGlobal(vertices.Length * sizeof(Point));
  var ipItems = new Stack<IntPtr>();

  try
  {
    Point** pArray = (Point**)ipArray.ToPointer();

    for (var i = 0; i < vertices.Length; i++)
    {
      var ipItem = Marshal.AllocHGlobal(sizeof(Point));
      ipItems.Push(ipItem);

      Marshal.StructureToPtr(vertices[i], ipItem, false);

      pArray[i] = (Point*)ipItem.ToPointer();
    }

    GetArea(pArray, vertices.Length, isClosed);
  }
  finally
  {
    IntPtr ipItem;
    while ((ipItem = ipItems.Pop()) != IntPtr.Zero) Marshal.FreeHGlobal(ipItem);

    Marshal.FreeHGlobal(ipArray);
  }
}