OpenNurbs and PInvoke

Hi,

I am testing methods written in C++ and calling them in C# via PInvoke.
If I compile dynamic .dll without OpenNurbs I can call them in C# console application without any problems. Same for other libraries.

I decorate OpenNurbs methods with this line:
#define PINVOKE extern "C" __declspec(dllexport)

But if I declare any type from OpenNurbs, build the C++, the C# application cannot even load or call any methods.

Can OpenNurbs be used with PInvoke and what I am missing?

If I comment out the ON_RTree or any other type, the PInvokeON.dll loads without issues and I can call e.g. Power method and get the result.

You have to define your exported functions as extern C to avoid name mangling

Gd

Thanks for the reply @gerryark .

What do you mean by this?
Should I change this PINVOKE Macro to something else:
#define PINVOKE extern "C" __declspec(dllexport)
I thought this is what I was doing.

Your functions exported as C are easily used by consumers. For C# there is a example somewhere in the rhino website on how to export and import in c#. Another issue to solve is marshal of parameters. I have a example that i can share but when back to office
Gd

Thank you it would be great to see the example.

I am trying to look at Moose by @dale but it seems that what I am doing is similar, but not working:
Moose/MooseCoreLib.h at 6 · dalefugier/Moose (github.com) It is really strange that the dynamic library with linked OpenNurbs cannot be loaded to C# even if I do not call that function.

On C# side I am decorating these methods using:
[DllImport(dllName, CallingConvention = CallingConvention.Cdecl)]

Are you trying to do this in a plugin or in a stand alone app that uses opennurbs?

I think a search for from unmanaged to manged code can give a lot of info on this subject.
Gd

I am trying to do this in a standalone C# Console Application. Is it actually possible yo use OpenNurbs as a library?

Absolutely; that’s what opennurbs was designed for. The vast majority of opennurbs has already been wrapped with C functions and pInvokes. This is what our Rhino3dm.NET library does.

All of the “C” functions that wrap opennurbs can be found at

We run a tool that we wrote called methodgen to generate C# files that are the pInvokes to these C functions

Thank you for the answer.

Are there any tutorials how to do this step by step for simple console app?

I assume it is not very straight forward as linking and wrapping header only libraries such as Boost, Eigen or CGAL. My primary goal is to use geometry types.

Does it mean that I need to download these project first and write code in these Visual Studio projects?

First I thought that it would be enough to link OpenNurbs with pragmas, but now I understand that wrapping OpenNurbs to .NET can be tricky. I am discussing just method calls not class calls from, just inputs of double and integer arrays.

Nope, this is a pretty obscure use case.

Ha, I don’t think I’ve ever heard people refer to integrating boost as straightforward :slight_smile:

It is probably easiest to just let me know what your functions look like that you want to expose. I can help get the “C” export version set up as well as the pInvoke declaration in C#

Dear @stevebaer ,

Thank you for helping me with this issue.

I uploaded 2 projects on Github to show what I am doing:

  1. C++ DLL project with OpenNurbs: OpenNurbsPInvoke/PInvokeON at main · ibois-epfl/OpenNurbsPInvoke (github.com)

All the C++ code is located in files: Export.cpp and Export.h

  1. C# DLL project calling the C++ DLL:
    OpenNurbsPInvoke/PInvokeCSharpOpenNurbs at main · ibois-epfl/OpenNurbsPInvoke (github.com)

There is only one file: PInvokeCSharpOpenNurbs.cs

The PInvoke methods works without problem for dummy function Power2 and I can read the result.
When I call the method UnsafeCollisionDetectionOBB PInvokeON.DLL cannot be loaded no more.
In this function I create ON_Tree data-type. The problem persists even if I delete all the code and leave one line 38 ON_RTree tree;

It works :tada:
I am really thankful that McNeel made the OpenNurbs open-source.

I thought that the code will be compiled in C++ application, but I had to copy paste opennurbs_public.dll. I have 2 follow up questions:

  1. Do I need both files or just opennurbs_public.dll ?
  2. Is there a way to compile C++ application into one .dll to avoid copy past these files in release folder?
    image
  3. I will be using Rhino with OpenNurbs communicating with .NET. Would it be possible to show how to transfer Rhino data type from C# to C++ and from C++ to C# without converting types to double or integer arrays for an Array of Polylines?

Hi @Petras_Vestartas,

We are currently not using Freetype in OpenNURBS at all. So there isn’t any reason to distribute freetype263.dll.

– Dale

1 Like

Thank you

@Petras_Vestartas are there still questions here?

Dear @stevebaer ,

Just one last one (thank you for your patience). I asked this question before, but still struggling to understand.

I really enjoy using OpenNurbs via PInvoke as a library. But I am doing this in a very simple way, by converting for example polylines to an array of doubles and then converting back to array of ON_Polyline in C++. I am aware that transferring large arrays can take some time.

How do you keep the same e.g. polyline object in memory and able to read both in C# and C++ without converting from one type to another? Is it possible to use OpenNurbs compiled as a library and use it in rhino, wouldn`t it cause conflicts with Rhino C++ objects?

I saw several examples Dale made with ON_Polyline for C++ and C#. But it is a bit complex to understand for me since I am beginner. Therefore I have a question if it is possible to show the most simple example for transferring an object between C# and C++ without conversion to integer or double array?

We create .NET classes that hold on to a pointer to unmanaged instances of C++ classes. The .NET class would create an instance of an unmanaged C++ class in it’s constructor and delete the instance in a Dispose function. For ON_Polyline, the .NET wrapper would have an IntPtr that represented an ON_Polyline*. The constructor would make a pinvoke into a function that looked like

ON_Polyline* ON_Polyline_New() {return new ON_Polyline();}

The C+ class could have a property called Count that makes a pInvoke.

public int Count
{
  get
  {
    // _ptrToPolyline gets set in constructor
    int count = UnsafeNativeMethods.ON_Polyline_Count(_ptrToPolyline);
    return count;
  }
}

and the C function would look like

int ON_Polyline_Count(const ON_Polyline* polyline)
{
  if (polyline)
    return polyline->Count();
  return 0;
}

The same pattern goes on and on and on… until you end up with RhinoCommon.

You wouldn’t want to do this. You would link to the opennurbs that ships with Rhino if you are writing code that is supposed to execute in that Rhino process. This is what C++ Rhino plug-ins do.

For linking the C++ types of Rhino in Visual Studio, I assume you do not link Rhino like this because you are using Rhino shipped geometry library :

// defining OPENNURBS_PUBLIC_INSTALL_DIR enables automatic linking using pragmas
#define OPENNURBS_PUBLIC_INSTALL_DIR "<MY_INSTALLPATH>"
// uncomment the next line if you want to use opennurbs as a DLL
//#define OPENNURBS_IMPORTS
#include "<MY_INSTALLPATH>/opennurbs_public.h"

And yet it is probably not the Rhino.rhp template. What kind of template or linking method I should use to make this work? (I do not use any other data types and functions except the ones written in OpenNurbs which I assume have equivalent code in current Rhino version)

I do not know how setup/link such PInvoke Visual Studio project for Rhino.

Just to be clear - if you are writing code that will run in Rhino, you should not be using the standalone version of openNURBS. Use the version of openNURBS that comes with Rhino. And do so from a Rhino C++ plug-in or from a Rhino-dependent DLL.

– Dale