PriorityLoad() not firing on Rhino for Mac

Hello all

I’m using PriorityLoad() to load embeded resources when starting Grasshopper up. This works well on Windows, but on Mac the method won’t fire because an error is thrown about missing assemblies before it even get’s to PriorityLoad().
It seems like Rhino/Grasshopper on Mac tries to load assemblies before my components are loaded for the first time, throwing an error on missing assembly and not giving me a chance to serve it with PriorityLoad(). I’ve tried everything even building on Mac.
Are the assemblies loaded differently on Grasshopper for Mac?

It looks like PriorityLoad also precedes loadings components on WIndows.

Thank you @gankeyu, I have no problem with this approach on Windows. The PriorityLoad() preceds the loading of my component and assemblies fine, allowing me to serve the resources as I want, but not on mac. Are you experiencing that too?

Don’t have a Mac. So I don’t know. :joy: I’d recommend you to post the entire error message.

But there’re differences in implementation across Mac and Windows. Recently I ran into something regarding GH_Document.

Ok, thank you. I just want to know if I should let it go and stop trying because of different implementations :wink: It’s driving me crazy!

This is my PriorityLoad snippet getting the resources on demand. The error is a simple assembly this-and-that not found and then it skips my component never getting to this code.

public override GH_LoadingInstruction PriorityLoad()
{
	RhinoApp.WriteLine(" !!!! PriorityLoad !!!!");
	AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

	return GH_LoadingInstruction.Proceed;
}

public Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
	AssemblyName assembly = new AssemblyName(args.Name);
	RhinoApp.WriteLine("Requesting assembly: " + assembly.Name);
	if (assemblies.Contains(assembly.Name))
	{
		return LoadStream(assembly.Name);
	}

	return null;
}

public Assembly LoadStream(string assemblyName)
{ ..........

So you never see this in the commandline?

No, I put it there to debug but the assembly it cannot find makes it skip my component completely. As i mentioned I think mac version wants to load my assemblies before PriorityLoad gets to fire which throws a missing assembly error and cancels the loading of my component all together.

On windows it does this (commandline) and everything is fine:

Command: Grasshopper
!!!! PriorityLoad !!!!
Requesting assembly: GhPython.resources
Requesting assembly: GhPython.resources
Requesting assembly: GhPython.resources
Requesting assembly: GhPython.resources
etc....`

On Mac I get this:

Command: Grasshopper
An error occured during GHA assembly loading:
Path: /Users/ita/Library/Application Support/McNeel/Rhinoceros/6.0/Plug-ins/Grasshopper 
(b45a29b1-4343-4035-989e-044e8580d9cf)/Libraries/LoadDLL.gha
Exception System.TypeLoadException: 
Message: Could not load type of field 'Parameter.Connector:<Factory>k__BackingField' (0) 
due to: Could not load file or assembly 'MessagePack, Version=5.0.0.0, Culture=neutral, 
PublicKeyToken=89e7d7c5feba84ce' or one of its dependencies.

Edit: If I place all my dll’s in the folder of my gha then it will find them when it needs them and after that go into priorityload, but that defeats the purpose for me.

Hi @thomas.william.lee,

The issue you might be running into here could be due to the way mono (the .NET runtime we use on Rhino for Mac) , vs. the .NET Framework on Windows. The technical detail is that mono will JIT compile an entire class and fail if it can’t find any referenced types, whereas on .NET Framework it can JIT compile one method at a time.

What I would suggest is to look at the class that is adding your handler for AppDomain.CurrentDomain.AssemblyResolve, and ensure it is not using any types in any method, property, or member that requires the assemblies you are trying to resolve.

Another option that might help is to move the code to add your AssemblyResolve handler to the static constructor of your class instead of using PriorityLoad(). For example:

class MyClass
{
  static MyClass()
  {
	AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
  }

  //...
}

Hope this helps!

The technical detail is that mono will JIT compile an entire class and fail if it can’t find any referenced types

Is Mono like that? I have the following code in one class, and it runs smoothly on Mono (at least last time I tried) :thinking:.

IIRC, in MacV5, there’s no parameterless constructor for GH_Document so calling new GH_Document() would crash Mono, when I tested about it two years ago.

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static GH_Document NewEmptyDocumentMac()
        {
            return (GH_Document)Activator.CreateInstance(typeof(GH_Document), Rhino.RhinoDoc.ActiveDoc);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static GH_Document NewEmptyDocumentNormal()
        {
            return new GH_Document();
        }


        private static GH_Document NewEmptyDocument()
        {
            return Config.IsMono ? NewEmptyDocumentMac() : NewEmptyDocumentNormal();
        }

Hi @curtisw, thank you so much for your reply. It makes sense, but I’m thinking that maybe I’m implementing the whole PriorityLoad() thing wrong. I tried your suggestions without any luck.
What I’m doing to load the embedded resource is that I’ve created a simple class which does nothing, but add the handler. It’s not part of any of my grasshopper components. Therefore it doesn’t hold any requirements for any particular assemblies. Below is the entire class that handles the loading of embedded assemblies.
Your other suggestions about the static constructor I didn’t get. If I don’t have the PriorityLoad() and put it in a static constructor nothing fires on startup in this class. Is it in a static constructor of a component I need to place it?

Thank you very much for your time!

using Grasshopper.Kernel;
using Rhino;
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

public class LoadDLL : GH_AssemblyPriority
{
	private readonly string[] assemblies = { "MessagePack", "Microsoft.Diagnostics.Tracing.EventSource", "System.Runtime.CompilerServices.Unsafe", "System.Threading.Tasks.Extensions", "System.ValueTuple" };

	public LoadDLL()
	{
	}

	public override GH_LoadingInstruction PriorityLoad()
	{
		RhinoApp.WriteLine(" !!!! PriorityLoad !!!!");
		AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

		return GH_LoadingInstruction.Proceed;
	}

	public Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
	{
		AssemblyName assembly = new AssemblyName(args.Name);
		RhinoApp.WriteLine("Requesting assembly: " + assembly.Name);
		if (assemblies.Contains(assembly.Name))
		{
			return LoadStream(assembly.Name);
		}

		return null;
	}

	public Assembly LoadStream(string assemblyName)
	{

		string embeddedResource = "RADii.dll." + assemblyName + ".dll";

		byte[] byteArray = null;
		Assembly assembly = null;
		Assembly curAsm = Assembly.GetExecutingAssembly();

		using (var stream = curAsm.GetManifestResourceStream(embeddedResource))
		{

			if (stream == null) throw new Exception(embeddedResource + " is not found in Embedded Resources.");

			byteArray = new byte[(int)stream.Length];
			stream.Read(byteArray, 0, (int)stream.Length);
			try
			{
				if (byteArray.Length > 0)
				{
					assembly = Assembly.Load(byteArray);
					Debug.WriteLine("Loaded assembly: " + embeddedResource);
				}
			}
			catch
			{
				Debug.WriteLine("Error loading assembly: " + embeddedResource);
			}
		}

		return assembly;			
	}

}