RhinoCore No window crashes on latest Rhino version

On version 8.10.24228.13001 of rhino
I’d updated from 8.4 I think

My test project using rhinoInside to resolve Rhino dependencies stopped working when creating a rhino core object

public void StartRhino()
{
    try
    {
        var args = new string[]
        {
            "/netfx"
        };

        _rhinoCore = new Rhino.Runtime.InProcess.RhinoCore(args,
            Rhino.Runtime.InProcess.WindowStyle.NoWindow);
    }
    catch (Exception ex)
    {
        Debug.Print($"{ex.Message}");
    }
}

it doesn’t throw an exception or anything it just kills the process

I’ve tried to uninstall and reinstall rhino, and it used to work in the prior version so I’m not sure what’s wrong

Hello @Christina3

  • Are you using the Rhino.Inside nuget library?
  • I am assuming this code is compiled using .NETFramework right?
  • Did it work with 8.4?

I have the Rhino.Inside nuget library,
it did work with 8.4
I haven’t changed anything since upgrading rhino

Is the Resolver started? See rhino-developer-samples/rhino.inside/dotnet/SampleHelloWorld/Program.cs at 7d108e10bad671ffe51b1820f4bb3ee5b95f1a39 · mcneel/rhino-developer-samples · GitHub

Yes, the resolver is initialized before it gets to that point

Ok. I would run this in debugger with both native and dotnet framework debuggers attached and see if you can capture the crash.

It seems like the native and dotnet debuggers were already enabled, and the test explorer only reports that the test host crashed

I see. So you are running rhino inside a testing framework? Let’s try a simpler setup to troubleshoot. Does this basic program load your installed Rhino?

The project you provided does seem to load rhino and run fine
I’m also using .net 6.0 I had to use the “/netfx” argument to get it to work before I upgraded rhino

I’m experiencing the same issue as @Christina3. The SampleHelloWorld works for me. I see that project targets net4.8. Our issue seems to be targeting net6.0. What has worked before the latest Rhino update was passing the var args = new string[] { "/netfx"}; to the RhinoCore() call. Admittedly that never did make any sense to me because “/netfx” means using NETFramework instead of NETCore, and net6.0 falls under NetCore.

So I guess the question is, what is the correct way to get RhinoInside working from a net6.0 project?

So I have a bit more info for you.

  1. Executing Rhino.Runtime.InProcess.RhinoCore(...) with WindowStyle.NoWindow Just fails silently without any exceptions being thrown or anything
  2. However, running it with WindowStyle.Normal or seemingly any other option will pop up an exception

    Here is the “Copy Details” from the exception in Visual Studio
System.Runtime.InteropServices.COMException
  HResult=0x80004005
  Message=Error HRESULT E_FAIL has been returned from a call to a COM component.
  Source=<Cannot evaluate the exception source>
  StackTrace:
<Cannot evaluate the exception stack trace>

And this happens regardless of if the args passed to RhinoCore is “/netfx” or “/netcore”.
The project setting up and using this test fixture is net6.0.

For an example project that has this same test fixture in it, see my post here: RhinoInside not working for unit tests after updating to Rhino 8.10.24228

Hi @Dustin_Condon, have you tried using .NET 7 (or 8?), passing “/netcore” to the startup? We have not ever done any serious testing with .NET 6 even though it sort of worked at one point.

There are numerous .NET Core specific assemblies now in Rhino 8 to resolve many issues, but almost all require .NET 7 or later.

Running .NET 6 with /netfx mode is an unsupported scenario with Rhino 8.

Hope this helps,
Curtis.

Sorry for the late response. Just had some time to come back to this. So I’ve setup a solution that contains separate projects that target:

  • net4.8
  • net6.0
  • net7.0
  • net8.0

respectively. What I’ve found is I can only get the net4.8 unit test to run. Every test other than the net4.8 one crashes here on the call to RhinoCore(...):

        /// <summary>
        /// Starting Rhino - loading the relevant libraries
        /// </summary>
        [STAThread]
        public void StartRhino()
        {
            var args = new string[]
            {
                "/netcore"
            };
            _rhinoCore = new Rhino.Runtime.InProcess.RhinoCore(args, Rhino.Runtime.InProcess.WindowStyle.NoWindow);
        }

Here’s a link to the source for this test solution: GitHub - dcondon-pmdi/RhinoTests: Example of using xunit test framework with Rhino.Inside to create unit test involving Rhino objects

Each project runs the same type of test, which basically does nothing:
image

Any help would be much appreciated

Kind regards,
Dustin

1 Like

I’ve also been experiencing this exact same issue. Any updates?

I use .Net 8 for my Rhino.Inside Console app. In the beginning I couldn’t use Rhino.Inside with .Net 7 or 8. Netframework 4.8.1 did work. After a lot of testing and debugging I think I managed to use .Net 8 rhino inside.

I do see that rhino loads certain managed dll assemblies that are not present. But the strange thing is that Rhino 8 is loaded in my console app .Net 8. I can also create a new headless rhinodoc, add and save rhino objects.

I did have to use several cheats to make sure that the custom: AppDomain.CurrentDomain.AssemblyResolve event is executed and I manually adjust the path to the rhinocommon.dll in runtime. I also added WPF support to my console app.

I have added a sample project in the attachment. Is the solution I found recommended to use, or are there any hidden pitfalls that may arise?

If you are going to test the project make sure you change the following path to your dll.


RhinoNetCoreLoader.zip (7.3 MB)

I have modified the previous project to make it a bit more user-friendly. Now you only have to enter the full path to your application in Settings.json. You can just use a Console app. Make sure that you do not use the exe of your application, but the dll.

I made another small adjustment. Because the CurrentDirectory has been adjusted to the rhino installation folder, any dlls of the application itself are no longer loaded. This problem has been resolved in this version

RhinoNetCoreLoader.zip (6.5 MB)

Another new version. With the previous version, subfolders of the Rhino installation were not found. So plugins like grasshopper were not loaded.

RhinoNetCoreLoader.zip (6.5 MB)

Hi there,

I just wanted to check in on this again. I’m wondering, are there other directories that I should be adding to the path before I call RhinoCore(…)? So far it’s only adding Rhino’s System directory. I.e. see the “//IS THIS SUFFICIENT FOR THE PATH?” comment block below.

The code block below is the entirety of what I’m doing to interact with RhinoInside (a test fixture). This is just the RhinoTests_net8/TestFixture.cs from GitHub - dcondon-pmdi/RhinoTests: Example of using xunit test framework with Rhino.Inside to create unit test involving Rhino objects. But besides the fact that it’s a test fixture, does the RhinoInside interaction look correct?

using System.Reflection;
using Microsoft.Win32;

namespace RhinoTests_net8
{
    /// <summary>
    /// Shared test context across unit tests that loads rhinocommon.dll and grasshopper.dll
    /// </summary>
    public class TestFixture : IDisposable
    {
        private bool initialized = false;
        private static string _rhinoDir = string.Empty;
        private Rhino.Runtime.InProcess.RhinoCore _rhinoCore;

        /// <summary>
        /// Empty Constuctor
        /// </summary>
        public TestFixture()
        {
            //get the correct rhino 8 installation directory
            _rhinoDir = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\McNeel\Rhinoceros\8.0\Install", "Path", null) as string ?? string.Empty;
            Assert.True(Directory.Exists(_rhinoDir), string.Format("Rhino system dir not found: {0}", _rhinoDir));

            // Make sure we are running the tests as 64x
            Assert.True(Environment.Is64BitProcess, "Tests must be run as x64");

            if (initialized)
            {
                throw new InvalidOperationException("Initialize Rhino.Inside once");
            }
            else
            {
                RhinoInside.Resolver.UseLatest = true;
                RhinoInside.Resolver.Initialize();
                initialized = true;
            }

            // -------------------------------------------------------------------------------
            // IS THIS SUFFICIENT FOR THE PATH?
            //................................................................................................
            // Set path to rhino system directory
            string envPath = Environment.GetEnvironmentVariable("path");
            Environment.SetEnvironmentVariable("path", envPath + ";" + _rhinoDir);
            //--------------------------------------------------------------------------------

            // Start a headless rhino instance using Rhino.Inside
            StartRhino();

            // We have to load grasshopper.dll on the current AppDomain manually for some reason
            AppDomain.CurrentDomain.AssemblyResolve += ResolveGrasshopper;

        }

        /// <summary>
        /// Starting Rhino - loading the relevant libraries
        /// </summary>
        [STAThread]
        public void StartRhino()
        {
            var args = new string[]
            {
                "/netcore"
            };

            _rhinoCore = new Rhino.Runtime.InProcess.RhinoCore(args, Rhino.Runtime.InProcess.WindowStyle.NoWindow);
        }

        /// <summary>
        /// Add Grasshopper.dll to the current Appdomain
        /// </summary>
        private Assembly ResolveGrasshopper(object sender, ResolveEventArgs args)
        {
            var name = args.Name;

            if (!name.StartsWith("Grasshopper"))
            {
                return null;
            }

            var path = Path.Combine(Path.GetFullPath(Path.Combine(_rhinoDir, @"..\")), "Plug-ins\\Grasshopper\\Grasshopper.dll");
            return Assembly.LoadFrom(path);
        }

        /// <summary>
        /// Disposing the context after running all the tests
        /// </summary>
        public void Dispose()
        {
            // do nothing or...
            _rhinoCore?.Dispose();
            _rhinoCore = null;
        }
    }

    /// <summary>
    /// Collection Fixture - shared context across test classes
    /// </summary>
    [CollectionDefinition("RhinoTestingCollection")]
    public class RhinoCollection : ICollectionFixture<TestFixture>
    {
        // This class has no code, and is never created. Its purpose is simply
        // to be the place to apply [CollectionDefinition] and all the
        // ICollectionFixture<> interfaces.
    }
}

Kind regards,
Dustin

Perhaps you can use this sample program for debuggin. This uses Net 8 with Rhino 8. The assembly resolver is not directly in the app that has the RhinoCommon.dll as a reference. Instead, a C# app is opened first that contains all the logic for the assembly resolver. Then the “RhinoCoreNet8.dll” (from this sample project) is called with invoke in runtime.

RhinoCoreNet8.zip (6.8 MB)

Furthermore, the assembly resolver app has a number of settings:


Let me know if you have any further questions about the assembly resolver

Thank you for your reply!

Sorry I’ve just gotten the time to look back into this. Would it be possible to share the source code for the assembly resolver? I’m just a little wary of using a random assembly. So what exactly does it do? You said it adds to the path so includes Rhino’s system directory? I’m a bit confused, because I would expect Rhino.Inside to handle that. But regardless, in my test fixture I already do add the path to Rhino’s system directory (see RhinoCore No window crashes on latest Rhino version - #19 by Dustin_Condon). So that can’t just be it.

Kind regards,
Dustin