Sharing Rhino Dependent C++ Dll

Hi,

I know this subject has been addressed couple of times, but as far as I saw it always ended with the reference to the examples like https://github.com/dalefugier/SampleNativeLibrary or SampleSharedUserData…and mostly to the perfect example: https://github.com/dalefugier/Moose . But just looking at the Moose example did not help me recreate it and make my own library. I tried to “copy” everything I saw in there but I am probably missing some steps. I think a small “tutorial” on this would be highly appreciated…just a short list of steps when dealing with a blank project.

If we imagine an empty VS Solution, and then add two C++ Rhino Plug-in Projects to it …how do we transform one of them into a CoreLib DLL and the other as the user of that DLL? I think the list could start like this:

CoreLib DLL:

  • in Properties/Target extension change from .rhp to .dll
  • use __declspec(dllexport) for the functions you want to export

PlugIn

  • #include “…\CoreLib\CoreLib.h” to the stdafx.h

What am I missing ? Any Linking? Is MooseCoreLibLinkingPragmas.h necessary? (Something like that doesn`t exist in other examples)

Thanks a lot!

1 Like

Speaking in regards to Moose, one important step is to declare MOOSECORELIB_DLL_EXPORTS as part of the core lib’s pre-processor definitions (Project → Properties → C/C++ → Preprocessor). This way, the macros defined in MoseCoreLib.h are for exporting, and for consuming plug-ins, the macros are for importing.

For this example, I generated the core lib project by running the plug-in wizard and removing plug-in specific stuff, such as the plug-in object, the sample command, and renamed the output to .DLL. The reason I did this is so I can use Rhino SDK functions from within the core lib project. If your core lib project doesn’t need access to Rhino, then you can run the MFD DLL wizard to create a regular MFC DLL. You can also run the standard Win32 project wizard and make a DLL project (too)

In this example, MooseCoreLibLinkingPragmas.h is used by plug-ins that include MooseCoreLib.h. It helps making the job of linking with the core lib easier.

Does this help?

Hi @dale, I tried making a GH plugin calling the c++ functions through MooseCommon but it’s not working…
Am I missing something?

Maybe. You might want to provide more information on what you’ve tried and/or post your code that isn’t working.

Sure. The code is as simple as that:

public class MooseSum : GH_Component
{
    public MooseSum()
        : base("MooseSum", "MSum", "Sum", "Moose", "Subcategory")
    {
    }

    protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
    {
        pManager.AddNumberParameter("A", "A", "A", GH_ParamAccess.item);
        pManager.AddNumberParameter("B", "B", "B", GH_ParamAccess.item);
    }

    protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
    {
        pManager.AddNumberParameter("R", "R", "R", GH_ParamAccess.item);
    }

    protected override void SolveInstance(IGH_DataAccess DA)
    {
        double a = double.NaN;
        double b = double.NaN;

        if(!DA.GetData(0, ref a))
            return;
        if (!DA.GetData(1, ref b))
            return;

        double r = MooseCommon.Utility.Sum(a,b);

        DA.SetData(0, r);
    }

    public override Guid ComponentGuid
    {
        get { return new Guid("{2a1e862f-40b3-46c7-80aa-b76c63c3c142}"); }
    }
}

I’ve built it using Debug64 configuration which builds for any CPU.
The error is:

Solution exception:Unable to load DLL ‘MooseCoreLib_x64.dll’: The specified module could not be found. (Exception from HRESULT: 0x8007007E)

Hi Lorenzo,

Make sure all referenced assemblies and their dependencies are in the same folder.

I’ve modified the Moose solution to include a sample GH component.

– Dale

Hi Dale,
I’ve tried yours and was still not working, but then I built it as Release instead of Debug and now both yours and mine work!

Do you know the reason for that?

Lorenzo

C++ Debug plug-in builds only work with Debug Rhino.

http://developer.rhino3d.com/guides/cpp/plugin-build-configurations/

1 Like

Hi,

I am back in the conversation because I still do not understand the complete example. How are Moose (C++ Plug-in) and MooseCoreLib connected? Only through #include “…\MooseCoreLib\MooseCoreLib.h” in the stdafx.h file?

Thanks!

Yes, this is correct. MooseCoreLib.h contains the exported function declaration, and it includes MooseCoreLibLinkingPragmas.h which contains the DLL library linking pragmas.

Thanks, I think I understand now. I just have on more question regarding this Dll sharing between C++ and C#.
I noticed that for different geometry types there has to be some sort of casting done… For example in Moose we have this for a point:

MOOSECORELIB_C_FUNCTION
ON_UUID MooseAddPoint( ON_3DPOINT_STRUCT point )
{
const ON_3dPoint* _point = (const ON_3dPoint*)&point;
return MooseSdkAddPoint( *_point );
}

Could you please give me an example of how this casting looks for couple of basic types (Brep, Curve, ON_ClassArray<ON_Brep>,…)? That would be really helpful, because I have C++ DLL functions that have Breps as an input and I am not sure how to call those from GH/RhinoCommon.

P.S. If there is similar casting procedure for function output (array of lines, curves, brep…) a hint on that would be appreciated too.

Thanks!

Many RhinoCommon wrapper classes hold onto pointers from native C++ objects. These cases are pretty simple, as you just pass an IntPtr back and forth.

For other structures, you will need some other helper structures that you can use to marshal data back and forth. For this, I suggest you download the RhinoCommon source from GitHub. With this, you can see what we do to pass data back and forth.

Hi Dale,

thanks, I will try to research it, but can you give me a small boost with an example? If I have a function in my DLL that takes Brep and an array of points as input and gives different arrays as output like this:

CORELIB_C_FUNCTION
int MyExportFunction(  ON_Brep* brep, bool x, int y, ON_3dPointArray& points,  //import
				     ON_3dPointArray& outpts, ON_ClassArray<ON_Line>& outlines );   //export

how does this call look from the UnsafNativeMethods.cs?

[DllImport("lib_x64.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int  MyExportFunction( ?, ? ,? )

Thanks!

Hi Toma,

You might want to take a look at methodgen, a tool we use to automatically generate Pinvoke signatures. Methodgen is included with the RhinoCommon source.

I’ve updated the Moose sample to demonstrate how you might wrap this kind of exported function. After reviewing, let me know if you have any questions.

– Dale

Hi Dale,

your example was very helpful. I think that methodgen might be a slight overkill, because I am not dealing with Enums and what I basically need is a “table” of C++ types and their equivalent C# types.

Based on your example I established a C#C++ communication. The only thing I didnt manage to send to C++ is Point3d[] array when used as an input (ON_3dPointArray& points in my function up that you didnt use when you made the new MousefFunction in the Moose example). The “out” arrays work fine, but the the same Point3D[] passed from .NET is empty in C++. My guess is that I am missing a single step in the UnsafeNativeMethods that “wrapps” the array, equivalent to the brep:

var const_ptr_brep = Interop.NativeGeometryConstPointer(brep);

What would be the equivalent? Can I pass a List of Point3d (instead of Points3d[]) maybe?

Thanks!
Toma

I’ve updated the Moose sample to demonstrate this.

Hi,

i have another small wrapping problem that belongs to the same subject of wraping NET methods for calling them from C++. The output arrays are wrapped in a following manner:

//point example
var points_array = new Rhino.Runtime.InteropWrappers.SimpleArrayPoint3d();
var ptr_points_array = points_array.NonConstPointer();

//line example
var lines_array = new Rhino.Runtime.InteropWrappers.SimpleArrayLine();
var ptr_lines_array = lines_array.NonConstPointer();

The question is, how do I wrap an array of polylines (ON_ClassArray<ON_Polyline>)? This is basically an array of 3D point arrays, so maybe the question is about wrapping 2D arrays…

Also, later in the DllImport method, do they also have the “IntPtr” form as other arrays?

Thanks!

Hi Toma,

Currently, RhinoCommon does not have an interop wrapper for an ON_SimpleArray of ON_Polyline objects. Thus, you might want to come up with one on your own.

I’d start with looking at the RhinoCommon source code to see how we implement classes like SimpleArrayPoint3d and SimpleArrayLine. Then, create your own class that represents ON_SimpleArray<ON_Polyline*>*.

– Dale

Thanks,

doesn`t seem so easy but I will try to work it out. There is a workaround though, I could use and generate a mesh instead of a polyline…is working with meshes and SimpleArrayMeshPointer wrapper the same as working with other (Point, line…)wrappers?

For example I am passing now IntPtr lines that gets transformed into ON_ClassArray< ON_Line >* lines and this works fine…I tried IntPtr meshes -> ON_ClassArray< ON_Mesh >* meshes and it doesnt really work…I use the following structure before and after the call to the Unsafe Method:

(out Mesh[] meshes enters the UnsafeNativeMethods main function)

var meshes_array = new Rhino.Runtime.InteropWrappers.SimpleArrayMeshPointer();
var ptr_meshes_array = meshes_array.NonConstPointer();

…call to UnsafeNativeMethod64 function with ptr_meshes_array as an argument…this goes as IntPtr meshes to C++ where it enters as ON_ClassArray< ON_Mesh >* meshes…

meshes = meshes_array.ToNonConstArray(); (There is no ToArray)

meshes_array.Dispose();

Updating the Moose example with a Mesh array would be great :slight_smile:

…another alternative would be a 2D Point3d array - if that is easier…can that be wrapped with existing methods?

Thanks!