Managing Multiple Rhino 8 Instances in Headless Mode

Hello everyone,

I’m developing a C# application that needs to run Rhinoceros 8 in headless mode to perform background processing tasks. Multiple instances of this application can run at the same time, each one launching a different Rhino instance.

To launch Rhino, I’m using code based on the Automation Sample in the following way:

private dynamic rhino;

try {
    const string rhino_id = "Rhino.Application.8";
    var type = Type.GetTypeFromProgID(rhino_id);
    rhino = Activator.CreateInstance(type);
} catch (Exception e){
   // exception handling
}

if (null == rhino) {
         // error handling
}

while (0 == rhino.IsInitialized()) {

    Thread.Sleep(100);

    time_waiting += 100;

    if (time_waiting > bail_milliseconds) {
         // error handling
    }
}
 

The Issue:

In some cases—especially when Rhino doesn’t have an active license or cannot open properly—my application hangs indefinitely during the CreateInstance call. The exception catch block is not triggered, so the application stays in the Activator.CreateInstance(type); line without handling the error.

Problem Details:

The behavior I’m seeing is unpredictable and inconsistent:

• Sometimes each instance of my process creates and launches Rhino one at a time. After about a minute, the Rhino instances close, and the exception is caught.

• Other times it takes 20 minutes, or it hangs indefinitely.

• Occasionally, multiple Rhino instances launch simultaneously, or Rhino continues running even after the calling process has been terminated.

This unpredictability is problematic because:

  1. I don’t receive a handle to the Rhino process from Activator.CreateInstance, so I can’t close specific instances directly.

  2. Rhino doesn’t terminate when my application closes, leading to accumulated instances over time, which could affect system performance.

Question:

How can I reliably detect and manage Rhino instances created by my application? Specifically:

• Is there a way to ensure Rhino closes when the calling process is terminated?

• How can I prevent multiple lingering Rhino instances if there’s an issue launching it?

Thank you in advance for any advice!

Hi @Claudio_Gianfrate that sample looks to be about 12 years old .

I’d consider using Rhino.Inside myself which will work a lot better and give you a lot more opportunities.

Thanks @CallumSykes for the response! I have some follow-up questions:

  1. When running Rhino.Inside, is the Rhino instance created as a child process? If not, how can I ensure it terminates when the caller process ends?

  2. Currently, our code loads a custom plugin (built with RhinoCommon) and executes its functions as shown below:

private dynamic rhino;

[...]
    rhino = Activator.CreateInstance(type);
[...]

// Set Rhino instance headless
rhino.Visible = 0;

// Get custom plugin
dynamic? plugin = null;
try {
    plugin = rhino.GetPluginObject("pluginID", "pluginID");
} catch (Exception e) {
    // exception handling
}

[...]
// Execute plugin functions
plugin.Function1(args);
plugin.Function2(args)

Could you advise on the correct way to load a plugin with Rhino.Inside?

  1. We’re already trying to refactor this to work with Rhino.Inside, but encountered a problem with the setup code:
class Program
  {
   
    static Program()
    {
      RhinoInside.Resolver.Initialize();
    }

    [System.STAThread]
    static void Main(string[] args)
    {
        using (new RhinoCore(args)) {
          //Do stuff...
        }
    }
  }

However, when running this, we get a runtime error:

Unhandled exception. System.IO.FileNotFoundException:
Could not load file or assembly ‘RhinoCommon, Version=8.12.24282.7001, Culture=neutral, PublicKeyToken=552281e97c755530’. The system cannot find the file specified.
File name: ‘RhinoCommon, Version=8.12.24282.7001, Culture=neutral, PublicKeyToken=552281e97c755530’

We’re using .NET 6.0, and while Rhino.Inside (Version 7.0.0) officially requires .NET Framework 4.8, I’ve seen forum posts suggesting this might not be a hard requirement. Could you clarify if there’s a way to resolve this or any workarounds?

You should be able to use RhinoCore.Dispose() with the RhinoCore which will get created when you use Rhino.Inside. You can see this being done with a using in the samples.

I believe I’ve always used Rhino.PlugIns.LoadPlugin(...) https://developer.rhino3d.com/api/rhinocommon/rhino.plugins.plugin/loadplugin to ensure it’s loaded.
As for grabbing the active instance. You should not create your own instance, Rhino will do that for you. Are you able to reference your plugin from this project? If not you’ll need to do some System.Reflection, or dynamics as you’ve done.

I’m also getting this with the sample so let me look into this.

I would suggest using net7.0 (over net6.0), that’s what Rhino 8 is using. Currently our Nugets are published to net48 only, but you can use those via netcore.

Thanks for the advice! I’ll give it a shot in a .NET Framework 4.8 project.

After some debugging, I think I’ve pinpointed the issue. When I install the latest RhinoInside (version 7.0.0), it automatically installs RhinoCommon, Grasshopper, and RhinoWindows at version 7.0.20314.3001. However, if a different version of RhinoCommon is already installed, RhinoInside attempts to link to that version instead. This causes a runtime error if the version is higher than 7.0.20314.3001, as RhinoInside 7.0.0 isn’t compatible with newer versions.

Additionally, RhinoCommon 7.0.20314.3001 isn’t compatible with .NET 6.0, so for now, switching to .NET Framework 4.8 seems to be the only way to get it working. Do you think upgrading to .NET 7.0 would make a difference here?

I had a similar (unsolved) Issue here:

would be nice to have an updated example.

1 Like