# bim-rhino > Rhino 6 automation via an in-process plugin. Executes C# against the live RhinoCommon API and controls the Rhino session from a terminal. ## How the connection works bim-rhino communicates with a Rhino plugin (`rhino-cli`) over a local TCP socket. The port is assigned dynamically at plugin startup and stored in `%LOCALAPPDATA%\bim-cli\instances\rhino-.json`. Multiple Rhino instances are supported — each gets its own port and instance file. **Rhino must be running with the plugin loaded.** Check with `bim rhino status`. If the plugin is not loaded, run `bim rhino install` then restart Rhino. ## Requirements - Windows 10/11 - Rhino 6 installed and licensed - bim-rhino plugin loaded (installed via `bim rhino install`, takes effect on next Rhino launch) ## Verb reference ### Session control - `bim rhino instances` — list all live Rhino sessions: pid, version, port, active document. Call this first when multiple Rhino copies are running. - `bim rhino status [--pid ]` — plugin state, active document path - `bim rhino open --path FILE [--timeout 60]` — open a .3dm in the running session - `bim rhino install` — register the plugin via `HKCU\SOFTWARE\McNeel\Rhinoceros\6.0\Plug-ins`; takes effect on next Rhino launch ### Code execution - `bim rhino exec --code "C# snippet"` — run inline C# against the live RhinoCommon API; returns JSON - `bim rhino exec --file script.cs` — run a .cs file - `bim rhino exec --timeout 60` — override execution timeout (default 30s) - `bim rhino exec --pid ` — target a specific Rhino instance when multiple are running ## exec globals These variables are pre-injected into every `exec` snippet. You do not declare them. | Variable | Type | Description | |----------|------|-------------| | `doc` | `Rhino.RhinoDoc` | active document; null when no document is open | | `app` | `RhinoAppProxy` | thin wrapper over `RhinoApp`; exposes `app.Version`, `app.Write()`, `app.RunScript()` | Rhino has no auto-transaction. All `exec` calls run directly on the UI thread via `RhinoApp.InvokeOnUiThread`. If your code modifies the document and you want undo support, begin a transaction manually: ```csharp doc.BeginUndoRecord("my operation"); // ... modify document ... doc.EndUndoRecord(undoRecordSerialNumber); ``` ## exec patterns **Read document info:** ``` bim rhino exec --code "doc?.Name ?? \"(new)\"" ``` **Count objects:** ``` bim rhino exec --code "doc.Objects.Count" ``` **Add a point:** ``` bim rhino exec --code "doc.Objects.AddPoint(new Rhino.Geometry.Point3d(0,0,0))" ``` **Run a Rhino command:** ``` bim rhino exec --code "app.RunScript(\"_Line 0,0,0 10,10,0\", false)" ``` **Target a specific instance:** ``` bim rhino instances bim rhino exec --pid 27120 --code "doc.Objects.Count" ``` ## Multi-instance workflow ``` bim rhino instances # → [{"pid":27120,"port":64623,"activeDocument":"site.3dm",...}, # {"pid":27596,"port":64682,"activeDocument":"building.3dm",...}] bim rhino exec --pid 27120 --code "doc.Objects.Count" bim rhino exec --pid 27596 --code "doc.Name" ``` ## Gotchas - Plugin must be installed with `bim rhino install` before the first run, and Rhino must be restarted after install for the plugin to load - `doc` is null when Rhino has no open document — check `doc == null` or use `doc?.Property ?? fallback` before accessing document properties - `RhinoCommon` geometry namespace (`Rhino.Geometry.*`) is thread-safe; all other RhinoCommon calls must be on the UI thread — `exec` handles this automatically - Rhino 6 uses .NET Framework 4.8; scripts compiled by Roslyn inside the plugin target net48, not .NET Core — most BCL APIs are available but some .NET 6+ APIs are not - `exec` cannot import external assemblies or NuGet packages — only RhinoCommon and the standard library are available - Long-running `exec` calls block the Rhino UI. Use `--timeout` to bound execution. ## Related pages - https://bimcli.com/llms.txt — full bim-cli reference - https://bimcli.com/revit/llms.txt — bim-revit docs (Revit automation)