Create function in C++ and import it to C#

Hello, I’m trying to create a library of functions in C++ and then use those functions in C#. This is how I do it.
CapMoose

CapMoose1

In this example in looking for the intersection between a Mesh and a line and returning the point where they intersect. The issue I’m having is that C++ and C# work with different types of objects, so my first function returns an ON_3DPoint but the import in C# expects a Point3d object, so it wont work.

Is there a way to solve this issue so I could pass different objects from C++ to C#?

Moved to Developer category

Hi @SirLegend7,

Writing interoperability code can be very complicated. I highly suggest Googling “P/Invoke” and reading up on the topic, as there is a lot to learn.

Since you are starting with the Moose solution, I can help with this.

On the C++ side, start with a robust function like this:

MOOSECORELIB_C_FUNCTION 
bool ON_MeshTree_IntersectLine(
  const ON_Mesh* pMesh, 
  const ON_Line* pLine, 
  ON_3dPointArray* pPoints
)
{
  bool rc = false;
  if (pMesh && pLine && pPoints)
  {
    const int points_count = pPoints->Count();
    const ON_MeshTree* pMeshTree = pMesh->MeshTree(true);
    if (pMeshTree)
    {
      ON_SimpleArray<ON_CMX_EVENT> cmx_events;
      const int cmx_count = pMeshTree->IntersectLine(*pLine, cmx_events);
      for (int i = 0; i < cmx_count; i++)
      {
        const ON_CMX_EVENT& cmx = cmx_events[i];
        if (cmx.m_type == ON_CMX_EVENT::cmx_point)
          pPoints->Append(cmx.m_M[0].m_P);
      }
    }
    rc = pPoints->Count() > points_count;
  }
  return rc;
}

Note, it’s not possible to pass custom classes between C++ and C#. But it is possible to pass class pointers. Many of the RhinoCommon classes just hold onto a pointer to the native Rhino or openNURBS class. So the above example just accepts points are arguments.

On the C# side, you will want to declare the unsafe native method like this:

[DllImport("MooseCoreLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool ON_MeshTree_IntersectLine(IntPtr pMesh, ref Line line, IntPtr pPoints);

An IntPtr is an integer which is the same size as a pointer. RhinoCommon uses IntPtr to store a pointer to native Rhino and openNURBS classes.

You can now wrap this unsafe native method for use like this:

public static Point3d[] MeshLineIntersection(Mesh mesh, Line line)
{
  if (null == mesh)
    throw new ArgumentNullException(nameof(mesh));
  if (null == line)
    throw new ArgumentNullException(nameof(mesh));

  using (var points_array = new Rhino.Runtime.InteropWrappers.SimpleArrayPoint3d())
  {
    var ptr_points = points_array.NonConstPointer();
    var ptr_const_mesh = Rhino.Runtime.Interop.NativeGeometryConstPointer(mesh);
    var rc = UnsafeNativeMethods.ON_MeshTree_IntersectLine(ptr_const_mesh, ref line, ptr_points);
    if (rc)
      return points_array.ToArray();
  }
  return new Point3d[0];
}

RhinoCommon contains some interop helper classes and methods that are helpful when trying to call into the Rhino C++ SDK.

Finally, you can call the new method like this:

var sphere = new Sphere(Plane.WorldXY, 5.0);
var mesh = Mesh.CreateQuadSphere(sphere, 4);
var line = new Line(new Point3d(-10, 0, 0), new Point3d(10, 0, 0));

var points = MooseCommon.Utility.MeshLineIntersection(mesh, line);

foreach (var pt in points)
  doc.Objects.AddPoint(pt);

Hope this gets you started.

– Dale

4 Likes

This works, thank you very much!