Engineering / Computers / Part 2 of 9
Connecting from Visual Studio
Moving from a macro to a compiled .NET program changes almost nothing about the API calls and almost everything about the plumbing around them. The logic transfers in an afternoon; the references, casting rules and object lifetime are where the days go if nobody has told you what to expect.
- Part 2 · Tooling
- Interop settings
- Attach vs launch
- COM lifetime
KL-ENG-COMP-1502 · KEVOS® Knowledge Library · Australian English
A macro is handed the application object because it runs inside the SOLIDWORKS process. An external program is not. It has to locate a running session through COM, or start one, and then work with objects that live in another process across an interop boundary. Everything distinctive about .NET SOLIDWORKS programming follows from that.
01Why leave the macro editor
The Visual Basic editor inside SOLIDWORKS is a capable environment for small tools. Four things push work out of it.
Real user interfaces
Multi-control windows, file dialogues, progress reporting, lists and trees — none of which the macro environment offers convincingly.
Access to everything else
Databases, spreadsheets, web services, version control, unit-testing frameworks and modern libraries. Automation rarely stays confined to CAD for long.
Compilation and type safety
Errors surface at build time rather than halfway through a batch of two hundred files.
The path to add-ins
An add-in is a .NET library. Everything you learn building a stand-alone client carries directly into Part 9.
02Project setup and the two settings that matter
Create a desktop application project, then add references to the SOLIDWORKS interop assemblies. They are installed alongside SOLIDWORKS, and they are also published as packages you can restore.
On each SOLIDWORKS interop reference, set Embed Interop Types to False. The default is True, and with it enabled the calls appear to compile but fail at run time in ways that are difficult to interpret. Setting Copy Local to False as well avoids shipping a copy of the interop that then disagrees with the installed release.
SOLIDWORKS is a 64-bit application. A client that attaches to it must be built for a matching architecture — either 64-bit explicitly, or a neutral target that will run as 64-bit on a 64-bit operating system. A 32-bit build is one of the more common causes of an attach that fails with an unhelpful message.
03Attaching to a running session
COM identifies registered applications by a program identifier. For SOLIDWORKS this is a stable string, and there are release-specific variants alongside it. Ask the running-object table for the current instance.
using SolidWorks.Interop.sldworks;
using System.Runtime.InteropServices;
ISldWorks swApp = null;
try
{
swApp = (ISldWorks)Marshal.GetActiveObject("SldWorks.Application");
}
catch (COMException)
{
// Nothing registered under that identifier: no session is running,
// or it is running under a different user or elevation level.
swApp = null;
}
if (swApp == null)
{
MessageBox.Show("Start SOLIDWORKS, then run this tool again.");
return;
}Two behaviours are worth committing to memory.
- Failure is an exception, not a null return. Calling without a try/catch produces an unhandled exception the first time the tool is run before SOLIDWORKS — which is exactly when a new user will run it.
- Elevation must match. A program running as administrator will not find a session started by a normal user, and the reverse is equally true. The symptom is an attach that fails while SOLIDWORKS is visibly open on screen.
The convenience wrapper for retrieving a running object is a .NET Framework facility and is not available in every newer runtime. Where it is absent, the same result is obtained by calling the underlying operating system function that reads the running-object table. The concept is unchanged; only the helper differs. Confirm what your chosen target framework supports before committing to it.
04Starting a session when none is running
For unattended batch work, requiring a user to have SOLIDWORKS already open defeats the purpose. Create an instance from the program identifier instead.
var swType = System.Type.GetTypeFromProgID("SldWorks.Application");
var swApp = (ISldWorks)System.Activator.CreateInstance(swType);
swApp.Visible = true; // omit for a genuinely headless batch runA newly created instance starts hidden. Leaving it hidden is right for a scheduled export job and wrong for anything a user is watching, because a hidden session that prompts for input appears to hang.
Every instance you start consumes a licence and keeps consuming it until the process ends. A batch tool that starts a session must also close it, on the failure path as well as the success path. Wrapping the whole run so that shutdown happens regardless of outcome is not optional — orphaned processes accumulate silently and exhaust licences at the worst possible time.
| Situation | Approach | Reason |
|---|---|---|
| User is modelling and wants a tool | Attach | Acts on what is already open; no second session, no second licence |
| Scheduled overnight batch | Launch | No user present; the tool controls the whole lifecycle |
| Either might apply | Attach, fall back to launch | Try to attach; on failure create an instance and record which path was taken |
| Tool must never disturb the user | Attach only | Launching a second session while one is open confuses everyone |
05Casting, and why so much is returned as object
A great many API members are declared as returning a generic object. This is a consequence of the interface being COM-based and having evolved over decades, and it means explicit casting is a constant feature of .NET SOLIDWORKS code.
IModelDoc2 swModel = swApp.ActiveDoc as IModelDoc2;
if (swModel == null) { /* nothing open */ return; }
// Type-specific behaviour comes from casting the same handle again.
if (swModel.GetType() == (int)swDocumentTypes_e.swDocASSEMBLY)
{
IAssemblyDoc swAssy = (IAssemblyDoc)swModel;
// ... assembly-only members are now reachable
}Prefer the safe cast that yields null over the hard cast that throws, then test the result. Failures here are usually a wrong assumption about document type rather than a genuinely exceptional condition, and a null test expresses that more honestly than an exception handler.
Members that return collections — configuration names, sheet names, child components — return an object that must be cast to an array before use. In VB.NET and VBA the conversion is implicit and the same code reads more simply; in C# the cast is explicit. This is the largest surface-level difference between the two languages when using this API, and it is cosmetic rather than functional.
06COM object lifetime
Every API object your program holds is a wrapper around something owned by the SOLIDWORKS process. The wrapper is managed by .NET; the object behind it is not. That asymmetry is the source of the most puzzling defects in .NET CAD automation.
The symptom
SOLIDWORKS refuses to close cleanly, or the process persists after the window disappears, or a file stays locked after processing. Almost always, a reference is still outstanding.
The cause
References are released when the garbage collector runs, which is at a time of its choosing. Until then, the other process considers the object in use.
The discipline
Release references explicitly when you are finished with them, particularly inside loops that process many documents, and null the variable afterwards.
The judgement
Do not release something you did not obtain, and do not release an object you will use again. Over-releasing produces failures that are harder to diagnose than the leak it was meant to prevent.
For a tool that opens one document and exits, ordinary cleanup is sufficient. For a tool that opens, processes and closes documents in a loop, release each document reference at the end of each iteration. The difference between the two patterns shows up at about the fiftieth file.
07Troubleshooting the first run
| What you see | Most likely cause | Action |
|---|---|---|
| Attach throws, SOLIDWORKS is open | Elevation mismatch, or a 32-bit build | Run both at the same level; confirm the platform target |
| Compiles, fails at the first call | Embed Interop Types left enabled | Set it to False on every SOLIDWORKS interop reference |
| Members missing from completion | Interop version does not match the installed release | Reference the interops from the installed release |
| Cast fails on a returned object | Assumed document type is wrong | Test the document type before casting |
| SOLIDWORKS will not close | Outstanding COM references | Release references explicitly; null the variables |
| Works on the developer machine only | Absolute paths, or an interop copied alongside the executable | Read paths from preferences; set Copy Local to False |
Note the SOLIDWORKS release, the interop version, the target framework and the platform target in the project's own documentation. When the tool eventually breaks on a new release, this half-page is what makes the diagnosis take an hour instead of a day.
