mcneel / rhino-developer-samples

Rhino and Grasshopper developer sample code
http://developer.rhino3d.com
Other
634 stars 333 forks source link

Cannot run RhinoInside+Grasshopper Samples, any configuration #100

Open edvinkuric opened 4 months ago

edvinkuric commented 4 months ago

Hello, i am not sure if this is the correct Repo for posting my issue, but:

i have trouble running Headless Rhino/RhinoInside with Grasshopper-Plugin.

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.

My Grasshopper-Scripts uses revit-internal api to generate a rvt based on a DXF-File. Can somebody help me with that or provide suggestions? That would be awesome - any hint helps

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

I also tried posting my issue in https://github.com/galer7/run-grasshopper-demo/issues/1 , but the owner-repo also said that the examples are broken since the rhino 8 update, as seen in his reply.

Is there any way possible to achieve running RhinoInside / Grasshopper to perform these Revit-Transformations? Any pointers or help is much appreciated.

Thank you very much!! BR, Edvin

dalefugier commented 4 months ago

@edvinkuric - can you move this to the Rhino.Inside category on Discourse?

https://discourse.mcneel.com/c/rhino-inside/110

Thanks,

-- Dale