galer7 / run-grasshopper-demo

🦗 Demos for running headless Grasshopper
1 stars 0 forks source link

Cannot run Samples, any configuration #1

Open edvinkuric opened 3 weeks ago

edvinkuric commented 3 weeks ago

Hello, i have trouble running any of the provided examples.

I get the following Exception, when using net481: InvalidCastException: Unable to cast COM object of type 'System.__ComObject' to class type 'Grasshopper.Plugin.GH_RhinoScriptInterface'.

When using dotnetcore 7, i can't even load the correct DLLs or receive an "Error: HResult E_Fail has been returned from a call to a COM component", which i can't debug further.

Are there any restrictions on using specific versions?

My setup is:

Ideally, i can run Rhino 8 Headless with dotnetcore 7 to be "future-proof", but i hadn't any luck for now. I would also be happy, if i can run any variant for starters, but no Rhino 7 Licenses can be bought anymore.

Can somebody help me with that or provide suggestions? That would be awesome - any hint helpsp

Here is the code, which i used/changed:

Program.cs: (adapted part) ` namespace RhinoGrassHopperRunner { class Program {

    static Program()
    {
        RhinoInside.Resolver.Initialize();
        Console.WriteLine("Initializing RhinoInside.Resolver");
    }

    static void Main(string[] args)
    {
        using (var core = new Rhino.Runtime.InProcess.RhinoCore())
        {
            RunHelper();
        }
    }

    // Currently need a separate RunHelper function so the .NET runtime won't attempt to load the
    // Grasshopper assembly until after RhinoCore has been created. This should be "fixable" in a
    // future version of the RhinoInside nuget package
    static void RunHelper()
    {
     // Extract definition to sample location as executable
     var assembly = typeof(Program).Assembly;
     string dir = System.IO.Path.GetDirectoryName(assembly.Location);
     var scriptName = "DXF2RVT.gh";
     string filePath = System.IO.Path.Combine(dir, scriptName);

     using (var resStream = assembly.GetManifestResourceStream("RhinoGrassHopperRunner." + scriptName))
     using (var outStream = new System.IO.FileStream(filePath, System.IO.FileMode.Create))
     {
         resStream.CopyTo(outStream);
     }
    var plugins = Rhino.PlugIns.PlugIn.GetInstalledPlugIns().OrderBy(x => x.Value);
    Console.WriteLine("plugins:", plugins.Select(x => x.Key + "  " + x.Value));
    // Start grasshopper in "headless" mode
    var plugin = plugins.FirstOrDefault(x => x.Value.Contains("Grasshopper")).Key;
    //if (string.IsNullOrEmpty(plugin))
    //{
    //    Console.WriteLine("Grasshopper not found");
    //    return;
    //}
    var pluginObject = Rhino.RhinoApp.GetPlugInObject(plugin);
    var type = pluginObject.GetType();
    var castedObject = (Grasshopper.Plugin.GH_RhinoScriptInterface)pluginObject;
    castedObject.RunHeadless();
    ....

`

Resolver.cs:

` using System.Reflection; using System.Runtime.InteropServices;

// Code from https://github.com/mcneel/compute.rhino3d/blob/8.x/src/compute.geometry/Resolver.cs namespace RhinoInside { public class Resolver { ///

/// Set up an assembly resolver to load RhinoCommon and other Rhino /// assemblies from where Rhino is installed /// public static void Initialize() { Console.WriteLine("Initializing RhinoInside.Resolver"); if (System.IntPtr.Size != 8) throw new Exception("Only 64 bit applications can use RhinoInside"); AppDomain.CurrentDomain.AssemblyResolve += ResolveForRhinoAssemblies; }

    static string _rhinoSystemDirectory;

    /// <summary>
    /// Directory used by assembly resolver to attempt load core Rhino assemblies. If not manually set,
    /// this will be determined by inspecting the registry
    /// 
    /// This is the C:/Program Files/Rhino 8/System directory on Windows
    /// This is the Rhinoceros.app/Contents/Frameworks directory on Mac
    /// </summary>
    public static string RhinoSystemDirectory
    {
        get
        {
            //return "C:\\Program Files\\Rhino 7\\System";
            // THIS USES THE RHINO 8 DIRECTORY
            if (string.IsNullOrWhiteSpace(_rhinoSystemDirectory))
                _rhinoSystemDirectory = FindRhinoSystemDirectory(); 
            return _rhinoSystemDirectory;
        }
        set
        {
            _rhinoSystemDirectory = value;
        }
    }

    /// <summary>
    /// Whether or not to use the newest installation of Rhino on the system. By default the resolver will only use an
    /// installation with a matching major version.
    /// </summary>
    public static bool UseLatest { get; set; } = true; // was true to use rhino8

    public static string AssemblyPathFromName(string systemDirectory, string name)
    {
        if (name == null || name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase))
            return null;

        // load Microsoft.macOS in the default context as xamarin initialization requires it there
        if (name == "Microsoft.macOS")
            return null;

        //if (name.Contains("Rhino") || name.Contains("Grasshopper"))
        //{
            name = name.Split(',')[0];
        //}
        if (name.Contains("Grasshopper"))
        {
            systemDirectory = "C:\\Program Files\\Rhino 7\\Plug-ins\\Grasshopper";
        }
        else if(name.Contains("Rhino") && systemDirectory == null)
        {
            systemDirectory = "C:\\Program Files\\Rhino 7\\System";
        }
        string path = null;
        if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
        {
            path = Path.Combine(systemDirectory, "RhCore.framework/Resources", name + ".dll");
        }
        else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            //path = Path.Combine(systemDirectory, "netcore", name + ".dll");
            if (!File.Exists(path))
            {
               path = Path.Combine(systemDirectory, name + ".dll");
            }
            if (!File.Exists(path))
            {
                var intPath = typeof(int).Assembly.Location;
                string directory = System.IO.Path.GetDirectoryName(intPath);
                path = Path.Combine(directory, name + ".dll");
                if (!File.Exists(path) || name.Contains(".Drawing") || name.Contains("WindowsBase"))
                {
                    int index = directory.IndexOf("NETCORE", StringComparison.OrdinalIgnoreCase);
                    directory = directory.Substring(0, index) + "WindowsDesktop" + directory.Substring(index + "NETCORE".Length);
                    path = Path.Combine(directory, name + ".dll");
                }
            }
        }
        return path;
    }

    static Assembly ResolveForRhinoAssemblies(object sender, ResolveEventArgs args)
    {
        string path = AssemblyPathFromName(RhinoSystemDirectory, args.Name);
        //// remove everything after first occurrence of ,
        //var cleanedName = path.Split(',');

        //path = cleanedName[0] + ".dll";

        if (File.Exists(path))
            return Assembly.LoadFrom(path);
        return null;
    }

    static string FindRhinoSystemDirectory()
    {
        var major = Assembly.GetExecutingAssembly().GetName().Version.Major;

        if (RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
        {
            string baseName = @"SOFTWARE\McNeel\Rhinoceros";
            using var baseKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(baseName);
            string[] children = baseKey.GetSubKeyNames();
            Array.Sort(children);
            string versionName = "";
            for (int i = children.Length - 1; i >= 0; i--)
            {
                // 20 Jan 2020 S. Baer (https://github.com/mcneel/rhino.inside/issues/248)
                // A generic double.TryParse is failing when run under certain locales.
                if (double.TryParse(children[i], System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out double d))
                {
                    if (d < 8.0)
                        continue;

                    versionName = children[i];

                    if (!UseLatest && (int)Math.Floor(d) != major)
                        continue;

                    using var installKey = baseKey.OpenSubKey($"{versionName}\\Install");
                    string corePath = installKey.GetValue("CoreDllPath") as string;
                    if (System.IO.File.Exists(corePath))
                    {
                        return System.IO.Path.GetDirectoryName(corePath);
                    }
                }
            }
        }

        return null;
    }
}

} ` .csproj: image

Thank you very much!! BR, Edvin

galer7 commented 2 weeks ago

Hey Edvin, just seeing your message now.

Unfortunately I've stopped working on this effort shortly after creating this repo & trying to get any minimal feedback/help from the McNeel discourse forum. I as well tried to buy the Rhino 7 to get back the samples that worked, but as you said that's not possible. TLDR: The Rhino 8 update killed pretty much all I had working :sweat_smile:

I'm glad you found this repo useful, and I'm sorry I can't help you further. I hope you can find a way to get the samples working again. If you do, please let me know, I'd be happy to hear about it.