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?
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.
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.
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);
}
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.
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
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.
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.
}
}
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.
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.