Creating a C#/C++ mixed Grasshopper projects in Visual Studio
The story begins during my development of two Grasshopper plugins,
IG-Mesh and
BeingAliveLanguage, for the
Rhino/Grasshopper platform. As both plugins required computationally demanding
algorithms to function and needs to use some C++ libraries, a technical
challenge has been hovering over my head for quite some time.
The problem comes as this: to transmit and process geometric data defined in the
Rhino environment, I need to bridge the C++ and C# sides through the
PInvoke approach for
Calling Native Functions from Managed Code.
This for now is the most standard way of connecting C++ code into any C#
project, and allows me to take advantage of various computational geometry
algorithms from the Computer Graphics community, where most libraries are
written in C++.
However, to allow the C++ side to recognize any Rhino-based geometry, I need
to make the project Rhino-dependent, i.e. adding some official libraries/SDK
provided by McNeel to my project so that I can process the data transmitted from
the C# side.
And this process turned out to be UNEXPECTEDLY unsmooth.
Below, I wrote out the steps of correctly setting up a Grasshopper plugin with
mixed C#/C++ code in Visual Studio 2022 environment, with some additional
explanations learned from my own experiments.
To begin working with C++ and C# in the Rhino/Grasshopper environment, you
need to install the official extension from McNeel and use it as a setup wizard
for creating a initial C# setup. This will allow you to initiate a Grasshopper
Plugin C# project.

Conventionally, you can just add a C++ project to the solution and connect it
to the C# project. However, when you want to send Rhino-based geometries
(Point3d, Mesh, Polylines, etc.), it requires openNURBS between the two
sides to avoid manually allocating unmanaged memory
(Marshal Class).
The tricky part is HOW to integrate openNURBS into your project.
To transmit data between managed code (C#) and unmanaged code (C++) for our
case, we need openNURBS (to contain the geometries), and RhinoCommon SDK (to
wrap the data and transmit between the two sides).
Based on the official forum 1, Mcneel builds two versions of openNURBS . The private version is shipped with Rhino, and the open-source version is provided to the public as source codes.
At beginning, I simply expected to just add openNURBS lib as a normal C++
library to my project, and then, DONE.
I was STUPID, and experienced many issues:
- The library provides no
CMakelist.txtand cannot be integrated into anyC++project with the common CMake system. - The developers has no interest to make it more accessible to package
management system like
vcpkg2. - The documentation is really limited and I've spent quite some time fight with errors like Freetype263 issue.
- ...
I finally made it work by applying some dirty tricks and customizing some of the header files in the library. And the solution was not robust:
- I need to build a separate openNURBS lib using VS2019 with a lot of customize settings (not supporting VS2022 at the time of writing).
- It is almost impossible to ingetrate the process into a cloud building system like Github Action.
- It is not easy to migrate the development environment to a different machine.
In the end, it turned out a better solution is to use the private version. You can create a Rhino-dependent DLL project following the steps below:
- Install Rhino C/C++ SDK (use Rhino's API for converting Rhino-based geometries into datatypes defined in its openNURBS library).
- Create a
Rhino C/C++ plugin project
inside your Grasshopper project.
- Important: As the time of writing, you need to use the
Rhino 7 Skintemplate to initiate the project. TheRhino 7 Plug-inhas an issue of not compiling exported.dlland cannot be used for Grasshopper.
- Important: As the time of writing, you need to use the
- In Visual Studio -> Solution Explorer, delete ALL default files generated
by the wizard, except for two files
stdafx.handstdafx.cpp.- For the case of a Grasshopper plugin, those files are unnecessary.
- In Visual Studio -> Property Manager, remove the
Rhino.Cpp.PlugInproperty page from both theDebug|x64and theRelease|x64build configurations, and add theRhino.Cpp.PlugInComponent.propsin the same way.- The property page are found in the
PropertySheetsfolder in the Rhino C++ SDK folder. - If you don't do this, the compiled library will be
.rhpinstead of.dll, as the project was initially generated as a Rhino plugin.
- The property page are found in the
Next, you can add you own code to the C# or C++ project in your VS Solution.
There is an example provided by the developers:
Moose Project.
(I tend to avoid this massy manual process at the beginning and turned towards the open-source version, but I was wrong...)
After the above setup, you should have set up a correct framework for your project. The rest is to code your own project. I'd like to just provide some explanations and examples here to help you a bit more.
On a high-level, any geometry that you transmit is stored in the memory, wrapped into a wrapper and and passed through.
In C#, this wrapping process can be achieved by using the wrapper function
provided by Rhino:
// for mesh IntPtr pMesh = Rhino.Runtime.Interop.NativeGeometryNonConstPointer(rhinoMesh); // for simple array var valCpp = new Rhino.Runtime.InteropWrappers.SimpleArrayDouble(scalarV);
Then, you can call any function that is defined in your C++ project and
imported with PInvoke approach inside your C# project. As the returned type
is usually raw, you also need to convert it back to a standard C# type before
use it or send it out to the Grasshopper Canvas:
// call the cpp func CppPort.cppFunc(pMesh, valCpp.ConstPointer(), resCpp.NonConstPointer()); // conversion to C# type var arrayDbl = resCpp.ToArray(); var mappedV = new List<double>(arrayDbl);
As I mentioned at the beginning, I've spent quite some time, trying to find a easy and stable solution for this problem. Though the current solution is not satisfactory, but at least it is stable enough both for migrating inbetween different development machines and for setting up cloud-based auto-deployment process (for instance, using Github Actions).
However, the manual setup process indeed feels a bit silly, considering that I need to first create a Rhino plugin and modify it into a normal Rhino-dependent DLL project. As far as I know, there was no official documentation for this until I asked in the McNeel forum.
In fact, for lighter usage (not involving intense opeartion on NURBS
geometries), the open-source version of openNURBS is sufficient. And if McNeel
can integrate it into vcpkg or any other package management system, or even
just provide a CMakelist.txt, the setup process will be much easier and
standard for C++ developers.
But they say they are NOT ready 2...