How to Programmatically Start and Initialize Rhino Inside Revit Using C#

Hi Rhino Dev Team,

I am currently developing a Revit Add-in using the RhinoInside.Revit.dll and RhinoInside.dll to start and initialize Rhino from within Revit, similar to how the Python scripts operate on the Revit “Rhino.Inside” tab. Inspired by a presentation by Ehsan, I have diligently reviewed all the documentation and examined most of the code in these DLLs. However, I am unable to find any public methods that allow me to invoke, initiate, or start up Rhino.

My goal is to create a button within Revit that, when clicked, would enable users to select Revit components and send them to a Rhino file by running a simple Grasshopper script. This add-in is being developed in C# on .NET Framework 4.8.

Would the developer team be able to provide any sample code or guidance on how to achieve this functionality? Any insights or examples would be greatly appreciated.

Thank you for your support.

Best regards,
Danny Bentley

@Danny_Saul_Bentley I tagged @kike here. He knows everything about Rhino Inside Revit :smiley:

1 Like

Hi Ehsan, Thank you for sharing this post with @kike. If a Revit add-in can’t launch Rhino and run a Grasshopper script then maybe I can provide the .gh file on a shared drive and run it using Python code. Do we have any documentation for tutorials on how to achieve this?

Does this help understand how to create buttons that can run grasshopper or rhino scripts: Rhino.Inside®.Revit

It helps. I had hoped that the DLL had an invoke or startup accessible method to start Rhino, run Rhino, and run Grasshopper script using c#, however maybe for ease it might make sense to use the Rhino.Inside.Revit Options / Scripts.

The is no reason that rhino dl could not be loaded by a record plugin. The code for loading rhino is in our reboot toolbar code. Rhino inside Revit is open source so you can find the calls we use here: GitHub - mcneel/rhino.inside-revit: This is the open-source repository for Rhino.Inside®.Revit

Simple loading samples can be found here: rhino-developer-samples/rhino.inside at 7 · mcneel/rhino-developer-samples · GitHub

Thank you. I should be able to find what I need in the Git.

Hi @Danny_Saul_Bentley,

As RiR is setup you don’t need to deploy Rhino.Inside.dll but Rhino Inside Revit itself. Then any call you make to RhinoCommon should start Rhino for you in the background.

The only nuget package you need to reference in your project is RhinoCommon, then from the Revit addin code just call it. It should work as long as RiR is installed.

Hi everyone,

I wanted to provide an update on my progress with programmatically starting and initializing Rhino Inside Revit using C#. With the help of the suggestions and adjustments, I was able to get the code working. However, I encountered an issue with the Enscape plugin. Here’s a summary of what was done and the current challenge:

Steps Taken to Get the Code Working

  1. Initialization Check: I included checks to see if Rhino Inside was already initialized. If not, the code provided a message to the user to start Rhino Inside manually using the ‘Start’ button in the Revit UI.
  2. Delay Mechanism: We implemented a retry mechanism with delays to ensure the file opens successfully after Rhino Inside is initialized. This was necessary because attempting to open the document immediately after initialization sometimes failed.
  3. Test Project: The test project involved a simple code to start Rhino, open a Rhino file, and convert the file to an OBJ format.

Issue with Enscape Plugin

After implementing the above solution, I encountered a new issue where Rhino displayed the following error related to the Enscape plugin:
Unable to load Enscape.Rhino7.Plugin.dll plug-in: initialization failed.
This error suggests that there may be a conflict between Rhino Inside and the Enscape plugin when initializing.

Future Plans

Next, I plan to implement and use Grasshopper scripts within a Revit add-in. You can follow my progress on my GitHub repository:

GitHub - dannysbentley/dwpEvolution

Thank you in advance for your assistance and for all the help so far. I look forward to any suggestions or solutions you might have to address these remaining issues.

Code for Reference for other users.

#region Namespaces
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Rhino;
using Rhino.FileIO;
using Rhino.PlugIns;
using System;
using System.IO;
#endregion Namespaces

namespace dwpEvolution
{
    [Transaction(TransactionMode.Manual)]
    public class CommandRevit2Rhino : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp?.ActiveUIDocument;
            Document doc = uidoc?.Document;

            if (doc == null)
            {
                message = "No active Revit document.";
                return Result.Failed;
            }

            // Define the hardcoded file path
            string filePath = @"C:\dwp_Rhino\donut.3dm";

            if (!File.Exists(filePath))
            {
                message = "The specified file does not exist: " + filePath;
                return Result.Failed;
            }

            try
            {
                // Ensure Rhino Inside is initialized by the user
                if (!IsRhinoInsideInitialized())
                {
                    TaskDialog.Show("Rhino Inside Not Started", "Please start Rhino Inside using the 'Start' button in the Revit UI before running this command.");
                    return Result.Cancelled;
                }

                // Proceed with Rhino document conversion
                return ExecuteRhinoConversion(filePath, ref message);
            }
            catch (Exception ex)
            {
                // Handle exceptions
                message = "An error occurred: " + ex.Message;
                return Result.Failed;
            }
        }

        private bool IsRhinoInsideInitialized()
        {
            // Check if RhinoCore is already initialized
            return Rhino.Runtime.HostUtils.RunningInRhino;
        }
        
        private Result ExecuteRhinoConversion(string filePath, ref string message)
        {
            try
            {
                // Retry mechanism to ensure the file opens
                int retries = 3;
                for (int i = 0; i < retries; i++)
                {
                    var rhinoDoc = RhinoDoc.Open(filePath, out bool isOpen);
                    if (isOpen)
                    {
                        // Save the model to wavefront obj
                        var fileObjPath = Path.ChangeExtension(filePath, ".obj");
                        var fowo = new FileObjWriteOptions(new FileWriteOptions())
                        {
                            MeshParameters = Rhino.Geometry.MeshingParameters.Default,
                            ExportMaterialDefinitions = false,
                            MapZtoY = true,
                        };

                        var result = FileObj.Write(fileObjPath, rhinoDoc, fowo);
                        if (result != WriteFileResult.Success)
                        {
                            message = $"Failed to save the OBJ file. Result: {result}";
                            return Result.Failed;
                        }

                        TaskDialog.Show("Conversion Successful", $"OBJ file created at: {fileObjPath}");
                        return Result.Succeeded;
                    }

                    // Wait before retrying
                    System.Threading.Thread.Sleep(2000);
                }

                message = "Failed to open the Rhino document after multiple attempts.";
                return Result.Failed;
            }
            catch (Exception ex)
            {
                message = "An error occurred during conversion: " + ex.Message;
                return Result.Failed;
            }
        }
    }
}