How To Debug/Recompile Without Restarting Rhino

I’ve seen a lot of threads about debugging and having to restart Rhino between sessions. I’m curious if there’s any updated tips and tricks as we knock on 2020’s door.

Have the plugin act as a shim that dynamically loads assembly → Rebuild → Reattach to process?
Anything with Rhino Inside?

Please check New way to load a C# .net plugin

Before I saw your comment, I was able to get a reflection shim to work. I just cloned the plugin signatures and replaced the logic with:

It works with two copies of VS open. The first has the shim project and is attached to Rhino. The second has the actual plugin. You can rebuild the actual plugin and the shim loads the fresh compile when the next command executes.

The shim could be 100% generated using a plugin’s signature to stub out the reflection calls. I’m not sure why VS is locking the obj\Debug\ files, but deleting them before loading the bin\Debug files works.

1 Like

Interesting approach.

And bonus points for using F#.

I think I’m going to keep going with this approach. You don’t have to do anything special to the actual plug-in using a debugger shim.

I’ll refactor and post the shim source in c#.

Man, I am rusty. A 10 yr hiatus from programming plus a new language = 16 hrs to get hello world with uninterrupted debugging.

This works perfectly. I put a c# shim template up on github.

There’s a couple of small quirks, so I also recorded a quick YT vid.

Basically you have a shim and a normal plugin. You want to load the shim and debug it. For the normal plugin, you want to disable it in Rhino through the _PluginManager (accidentally running the normal command will prevent you from rebuilding) and unload the project in VS (force it to use the debug symbols supplied through reflection).

7 Likes

@EricM I’ve just tested this for Rhino 7 and Visual Studio 2017, and it works great with the following tweaks.

EricM was using Rhino 7 WIP, so if you’re using a different version (Rhino 7, Rhino 8 WIP), you’ll need to change a few file paths:

Add the following assembly references for both Plugin and PluginShim:

  • C:\Program Files\Rhino 7\System\Eto.dll
  • C:\Program Files\Rhino 7\System\Rhino.UI.dll
  • C:\Program Files\Rhino 7\System\RhinoCommon.dll

In the PluginShim project properties → Debug → Start action → Start external program, update the path to Rhino.exe:

Everything else worked as described in his YT video–thanks EricM!

3 Likes

Not that it matters much for .NET, but we’re currently using Visual Studio 2019 to develop Rhino (already since some SR of 6).

Ah, thanks for letting me know.

I’ve just tested this for Visual Studio 2019, and the instructions are identical to my previous post. The .sln file provided by EricM is for VisualStudioVersion = 16.0.29613.14, which is 2019, so yeah, I was the one behind the times here.

Will it work for Grasshopper plugin?

I don’t use Grasshopper. Maybe @nathanletwory would know?

Sorry, I don’t trust edit&continue type of technologies. I prefer to close Rhino, compile my work and restart Rhino.

As such I have not tried any of the solutions that have been surfaced on our forum.

I don’t know if any of the other Rhino devs, or plug-in devs have used this technique in GH (or in Rhino for that matter). @stevebaer @DavidRutten @curtisw @dale anyone?

I do what @nathanletwory does…

– Dale

To be fair, my shim isn’t edit and continue. It’s a full recompile to change something. You just don’t have to restart Rhino because the shim loads the freshly recompiled assembly.

1 Like

Hi @EricM ,

I was trying your shim approaching, while running into some exceptions.

I am using the latest github repo. And did the following:

  1. samplePlugin pdb and rhp are copied to original _shimCopy and _rhinoCopy.
  2. InShimCmd1.cs, the four fields were updated. But I am a little confused, because currently I put class name in the commandName field. Besides, my namespace has a dot.

image

The rest remained unchanged. Would be great if you can point me to the right direction in tackling this.

Regards,

In the target command, do you have a SetShimInstance method?


public class MirrorXYZ : Command
    {
        public static Command Instance { get; private set; }

        public MirrorXYZ()
        {
            Instance = this;
        }

        private void SetShimInstance(Command shim)
        {
            Instance = shim;
        }

I’m trying to remember how that bit of plumbing works. When there’s History, Rhino uses Command.Instance to log the proper plugin command to the HistoryTable. But that doesn’t work with a plugin assembly loaded via reflection. I think I used SetShimInstance to overwrite the default Instance definition (this) with the shim’s. Then when Rhino calls ReplayHistory() on the shim, the shim reloads your target assembly and executes the command there.

In the end I commented out the part that works on history replay (the last 5 lines). I guess it is because I have not implemented a method for history replay. After that everything works great.

Thanks for sharing!

Vincent