You just built a SolidWorks add-in. Before it can run, you need to register it as a COM server with regasm.exe, write registry keys under HKEY_LOCAL_MACHINE, and make sure your class has [ComVisible(true)] and a [Guid] attribute. You need admin privileges. You’re locked to .NET Framework 4.x. And if the registration goes wrong, you get a cryptic swRegistrationError and nothing loads.
Meanwhile, your colleague building an Inventor add-in drops a .addin XML file into a folder, points it at a .NET 8 assembly, and it just works. No regasm, no registry, no admin rights.
This isn’t a minor difference. It’s a fundamental architectural divergence in how two major CAD platforms handle extensibility. We decompiled the add-in loading DLLs from both products to understand exactly how each one works — and to find practical ways for SolidWorks developers to escape the .NET Framework trap.
How SolidWorks Loads Add-Ins: The COM Pipeline
SolidWorks discovers add-ins the same way Windows discovers COM servers — through the registry. The entire pipeline is built on CoCreateInstance, the Win32 function that creates COM objects from their Class ID.
Here’s the exact sequence that happens when SolidWorks starts:
SolidWorks reads
HKEY_LOCAL_MACHINE\SOFTWARE\SolidWorks\AddIns\and enumerates every subkey. Each subkey is a CLSID GUID like{AA151FF9-531B-46F3-B961-4BCBC9BC56F6}.For each CLSID, it calls
CoCreateInstancewithCLSCTX_INPROC_SERVER— meaning the add-in DLL is loaded directly into the SolidWorks process.Windows looks up
HKCR\CLSID\{GUID}\InprocServer32to find the DLL. For .NET add-ins, this is alwaysmscoree.dll— the .NET Framework CLR hosting shim.mscoree.dllreads theClass,Assembly, andRuntimeVersionvalues from the same registry key, bootstraps the .NET Framework CLR (versionv4.0.30319), and creates the managed object.SolidWorks calls
QueryInterfaceon the resulting object, asking for theISwAddininterface (GUIDda306a0d-eac5-4406-8610-b1da805d9270).If that succeeds, SolidWorks calls
ISwAddin::ConnectToSW(ThisSW, Cookie), passing a pointer to theISldWorksapplication object and a numeric cookie that identifies the add-in.
Every .NET SolidWorks add-in on the machines we inspected — CadShift, CAD Booster’s Lightning, third-party tools — all register with the same pattern: InprocServer32 = mscoree.dll, RuntimeVersion = v4.0.30319.
What a SolidWorks Add-In Class Looks Like
[Guid("AA151FF9-531B-46F3-B961-4BCBC9BC56F6")]
[ComVisible(true)]
public class MyAddin : ISwAddin
{
public bool ConnectToSW(object ThisSW, int Cookie)
{
var swApp = (ISldWorks)ThisSW;
swApp.SetAddinCallbackInfo2(0, this, Cookie);
// Create command groups, tabs, property manager pages...
return true;
}
public bool DisconnectFromSW()
{
// Cleanup: remove menus, release COM references, force GC
return true;
}
[ComRegisterFunction]
public static void Register(Type t)
{
var key = Registry.LocalMachine.CreateSubKey(
$@"SOFTWARE\SolidWorks\AddIns\{{{t.GUID}}}");
key.SetValue(null, 1);
key.SetValue("Title", "My Add-In");
key.SetValue("Description", "Does things");
}
[ComUnregisterFunction]
public static void Unregister(Type t)
{
Registry.LocalMachine.DeleteSubKey(
$@"SOFTWARE\SolidWorks\AddIns\{{{t.GUID}}}", false);
}
}
The [ComRegisterFunction] and [ComUnregisterFunction] methods are called by regasm.exe during registration. They create the SolidWorks-specific registry keys that tell SolidWorks “this COM server is an add-in, here’s its title and description.”
The Registry Footprint
A single SolidWorks add-in creates entries in at least three registry locations:
| Location | Purpose |
|---|---|
HKLM\SOFTWARE\Classes\CLSID\{GUID}\InprocServer32 | Standard COM registration (DLL path, class name, runtime version) |
HKLM\SOFTWARE\SolidWorks\AddIns\{GUID} | SolidWorks discovery (title, description, icon path) |
HKCU\Software\SolidWorks\AddInsStartup\{GUID} | Per-user startup preference |
This is why installing a SolidWorks add-in typically requires an MSI installer or an elevated command prompt running regasm /codebase MyAddin.dll.
The ISwAddin Interface
The ISwAddin interface lives in SolidWorks.Interop.swpublished.dll and has exactly two methods:
| Method | Parameters | Description |
|---|---|---|
ConnectToSW | object ThisSW, int Cookie → bool | Called when SolidWorks loads the add-in. Receives the application object and a unique ID. |
DisconnectFromSW | (none) → bool | Called when SolidWorks is closing or the user disables the add-in. Pre-notification for cleanup. |
Every class implementing ISwAddin must also implement several COM-adjacent interfaces from swpublished: IPropertyManagerPage2Handler7 for property manager pages, ISwCalloutHandler for callouts, and others. All require [ComVisible(true)].
Load Error Codes
When something goes wrong, ISldWorks::LoadAddIn returns one of these values from swLoadAddinError_e:
| Error | Value | Meaning |
|---|---|---|
swSuccess | 0 | Loaded successfully |
swAddinNotLoaded | 1 | General load failure |
swAddinAlreadyLoaded | 2 | Already in memory |
swFileNotFound | 3 | DLL not found at registered path |
swAddinsDisabled | 4 | User disabled all add-ins |
swLoadConflict | 5 | Version or dependency conflict |
swRegistrationError | 6 | COM registration is broken |
swLicenseError | 7 | License validation failed |
swUnknownError | -1 | Something else went wrong |
Error code 6 — swRegistrationError — is the one SolidWorks developers see most often. It means CoCreateInstance failed because the COM registration is missing, corrupt, or pointing to a DLL that can’t be loaded.
How Inventor Loads Add-Ins: The Manifest Pipeline
Inventor takes a completely different approach. Since Inventor 2012, it has used XML-based .addin manifest files instead of COM registry entries. By Inventor 2022, registry-based add-in loading was fully retired — manifests are the only path. We found 67 of these in a stock Inventor 2026 installation.
Discovery Paths
Inventor scans these directories for .addin files at startup:
| Location | Purpose |
|---|---|
C:\Program Files\Autodesk\Inventor 2026\Bin\Addins\ | Built-in add-ins (67 files) |
C:\ProgramData\Autodesk\ApplicationPlugins\*.bundle\Contents\ | Machine-wide third-party add-ins |
%AppData%\Autodesk\Inventor 2026\Addins\ | Per-user add-ins |
%AppData%\Autodesk\ApplicationPlugins\ | Per-user application plugins |
No registry involved. To install an add-in, you drop a .addin file and the DLL into one of these folders. To uninstall it, you delete them. To deploy per-user without admin rights, you use the %AppData% path.
The .addin Manifest Format
Here’s what a manifest looks like:
<Addin Type="Standard">
<ClassId>{E357-YOUR-GUID-HERE}</ClassId>
<ClientId>{E357-YOUR-GUID-HERE}</ClientId>
<DisplayName Language="1033">My Inventor Add-In</DisplayName>
<Description Language="1033">Does useful things</Description>
<Assembly>MyAddIn.dll</Assembly>
<LoadOnStartUp>1</LoadOnStartUp>
<LoadBehavior>0</LoadBehavior>
<UserUnloadable>1</UserUnloadable>
<Hidden>0</Hidden>
<SupportedSoftwareVersionGreaterThan>27..</SupportedSoftwareVersionGreaterThan>
<DataVersion>1</DataVersion>
<UserInterfaceVersion>1</UserInterfaceVersion>
</Addin>
Everything that SolidWorks puts in the registry — title, description, load behavior, version constraints — Inventor puts in this XML file. The <ClassId> GUID is used internally by Inventor’s loader to match types; it is not looked up in the Windows registry.
Translator add-ins get additional declarative elements:
<Addin Type="Translator">
<!-- ...standard elements... -->
<SupportsOpen>1</SupportsOpen>
<SupportsSaveCopyAs>1</SupportsSaveCopyAs>
<SupportsSaveCopyAsFrom>.ipt;.iam</SupportsSaveCopyAsFrom>
<FileExtensions>.glb;*.gltf</FileExtensions>
<FilterText Language="1033">glTF Files (*.glb)</FilterText>
</Addin>
In SolidWorks, you’d register translator capabilities programmatically through API calls at runtime. Inventor lets you declare them in the manifest.
The ClrAddinLoader: How Inventor Bridges Native and Managed Code
This is where it gets interesting. We decompiled Inventor’s ClrAddinLoader.dll and found the exact mechanism that makes manifest-based loading work without COM registration.
Inventor ships two CLR add-in loaders side by side:
| DLL | Target Framework | Purpose |
|---|---|---|
ClrAddinLoader.dll | .NET 8.0 | Modern add-ins |
ClrAddinLoader48.dll | .NET Framework 4.8 | Legacy add-ins |
Both export the same four functions:
DllCanUnloadNow
DllGetClassObject
DllGetClassFactory
DllSetClrAssemblyFileSpec ← the key function
Here’s the loading pipeline we reconstructed from the decompiled IL:
Step 1: Discovery. Inventor’s native RxAddIns.dll parses every .addin manifest file from the discovery paths. It reads the <Assembly> and <ClassId> elements.
Step 2: Assembly path injection. For managed add-ins, RxAddIns.dll calls ClrAddinLoader.dll!DllSetClrAssemblyFileSpec(path), passing the full path to the .NET assembly specified in the manifest.
Step 3: Class factory creation. RxAddIns.dll calls ClrAddinLoader.dll!DllGetClassObject(clsid, riid, ppv) with the CLSID from the manifest. Inside ClrAddinLoader, the Loader_DllGetClassObject method:
- Verifies the assembly file exists on disk via
File.Exists() - Creates a
ClrAddinClassFactoryinstance (a COMIClassFactoryimplementation) - Returns it as an
IClassFactorypointer
Step 4: Instance creation. When IClassFactory::CreateInstance is called, ClrAddinLoader runs CreateClrInstance(), which:
- Calls
Assembly.LoadFrom(assemblyFileSpec)to load the managed DLL - Iterates through
assembly.GetExportedTypes() - For each type, checks
type.GetInterface("Inventor.ApplicationAddInServer") - Compares
type.GUIDagainst the CLSID from the manifest - On match: calls
assembly.CreateInstance(type.FullName) - Returns the object via
Marshal.GetIDispatchForObject()
Step 5: Activation. Inventor calls ApplicationAddInServer.Activate(site, firstTime) on the add-in, passing an ApplicationAddInSite that provides access to the Application object.
The critical insight: Inventor is doing its own COM activation. It doesn’t call CoCreateInstance. It doesn’t look anything up in the Windows registry. The ClrAddinLoader is a custom COM class factory that uses .NET reflection to find and instantiate the correct type. The CLSID in the manifest is just a lookup key used internally by the loader — it never touches HKCR\CLSID.
The ApplicationAddInServer Interface
Inventor’s equivalent of ISwAddin is ApplicationAddInServer (GUID: E3571293-DB40-11D2-B783-0060B0F159EF):
interface ApplicationAddInServer
{
void Activate(ApplicationAddInSite AddInSiteObject, bool FirstTime);
void Deactivate();
void ExecuteCommand(int CommandID);
object Automation { get; }
}
Functionally similar to SolidWorks’ ISwAddin, but without any COM registration requirements. The class that implements this interface needs a [Guid] attribute (to match the <ClassId> in the manifest), but it doesn’t need [ComVisible], doesn’t need [ComRegisterFunction], and doesn’t need to be registered with regasm. The ClrAddinLoader finds it by scanning exported types for the interface match.
For .NET Framework add-ins, Inventor historically used .X.manifest files embedded into the DLL (with a <clrClass> element) for registration-free COM. With .NET 8 in Inventor 2025+, this was replaced by the standard EnableRegFreeCom MSBuild property, which auto-generates the manifest. Either way, the system-level COM registry is never touched.
Load Behavior Enums
Inventor offers granular control over when add-ins load:
| Enum Value | Code | Description |
|---|---|---|
kLoadImmediately | 94721 | Load at Inventor startup |
kLoadWithParts | 94722 | Load when a part document opens |
kLoadWithAssemblies | 94723 | Load when an assembly opens |
kLoadWithPresentations | 94724 | Load when a presentation opens |
kLoadWithDrawings | 94725 | Load when a drawing opens |
kLoadOnDemand | 94726 | Load only when explicitly requested |
SolidWorks has a single binary choice: load at startup or don’t. Inventor lets add-ins defer loading until a specific document type is opened — reducing startup time for add-ins that only work with certain file types.
The Architecture Side by Side
Here’s what the two approaches mean in practice:
| Aspect | SolidWorks | Inventor 2026 |
|---|---|---|
| Discovery | Windows Registry (HKLM\SOFTWARE\SolidWorks\AddIns) | .addin XML manifest files in known directories |
| Registration | regasm.exe for .NET, regsvr32 for C++ | Drop files in a folder |
| .NET support | .NET Framework 4.x only (via mscoree.dll shim) | .NET 8 (ClrAddinLoader.dll) and .NET Framework 4.8 (ClrAddinLoader48.dll) |
| Admin rights | Required (HKLM registry writes) | Not needed for per-user add-ins |
| Deployment | MSI installer or elevated regasm | xcopy deployment |
| Load control | Binary (startup or not) | Per-document-type + on-demand |
| Localization | Manual resource handling | Built into manifest (Language attribute) |
| Version filtering | Manual checking in code | Declarative <SupportedSoftwareVersionGreaterThan> |
| Uninstall | regasm /u + registry cleanup | Delete the files |
| COM activation | CoCreateInstance via Windows COM runtime | Custom ClrAddinLoader class factory via reflection |
The fundamental difference is that SolidWorks delegates add-in loading to the Windows COM infrastructure, while Inventor built its own loading infrastructure that uses COM internally but doesn’t require global COM registration.
Why This Matters for Developers
The COM registration requirement doesn’t just mean extra deployment steps. It creates a chain of technical constraints:
1. .NET Framework lock-in. The mscoree.dll shim that SolidWorks relies on is a .NET Framework component. When CoCreateInstance creates a .NET add-in, it goes through mscoree.dll → CLR v4.0.30319 → your assembly. This entire chain assumes .NET Framework. There is no mscoree.dll equivalent for .NET 8.
2. No side-by-side .NET versions. Because the CLR is loaded in-process via mscoree.dll, every .NET Framework add-in shares the same runtime. You can’t have one add-in on .NET 4.6 and another on .NET 4.8 — the first one loaded wins. Inventor’s dual-loader architecture (ClrAddinLoader.dll for .NET 8, ClrAddinLoader48.dll for .NET 4.8) explicitly solves this.
3. Admin privileges for installation. Writing to HKLM requires elevation. This means every add-in installation either needs an MSI running as admin or a user with admin rights running regasm manually. Inventor’s per-user %AppData% path requires no elevation.
4. Registry pollution. Each add-in creates entries in multiple registry hives. Over time, failed installations, version upgrades, and unclean uninstalls leave orphaned entries that can cause swRegistrationError (error code 6). Inventor’s file-based approach has no persistent state outside the files themselves.
5. No xcopy deployment. You can’t just copy a SolidWorks add-in DLL to another machine and have it work. It needs to be registered first. Inventor add-ins are xcopy-deployable.
How to Use Modern .NET in SolidWorks Add-Ins Today
SolidWorks hasn’t followed Inventor’s lead, but that doesn’t mean you’re completely stuck on .NET Framework 4.x. Here are practical approaches, ordered by maturity.
Approach 1: .NET 8 COM Hosting (Best Option Today)
.NET 5+ introduced native COM hosting through a comhost.dll mechanism. Instead of relying on mscoree.dll, you build a native COM server DLL that bootstraps the CoreCLR runtime.
How to set it up:
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<EnableComHosting>true</EnableComHosting>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
When you build this project, MSBuild produces three files:
MyAddin.dll— your managed assemblyMyAddin.comhost.dll— a native DLL that hosts the CoreCLRMyAddin.deps.json— runtime dependency manifest
Register the COM host with regsvr32 MyAddin.comhost.dll (not regasm). This creates the standard COM registry entries, but instead of pointing to mscoree.dll, they point to your comhost.dll, which knows how to bootstrap the .NET 8 runtime.
Your add-in class still needs [ComVisible(true)] and [Guid], and you still need [ComRegisterFunction] to create the SolidWorks-specific registry keys. But your actual code runs on .NET 8, with access to modern C# features, System.Text.Json, Span<T>, and everything else.
Caveats:
- The xCAD.NET framework already supports this approach and handles the registration boilerplate
- Some third-party libraries may have compatibility issues when running as an in-process COM server in a native host process
EnableComHostingonly supports traditional[ComVisible]/[ComImport]interop — the newer[GeneratedComInterface]source generator is not compatible- Only one .NET Core runtime can be loaded per process — if another add-in also uses
comhost.dllwith a different .NET version, you’ll get conflicts
Approach 2: Hybrid Out-of-Process Architecture
Run your business logic in a separate .NET 8 process and communicate with a thin in-process add-in via IPC.
┌─────────────────────────┐ Named Pipes ┌──────────────────────┐
│ SolidWorks Process │ or gRPC │ .NET 8 Process │
│ │◄────────────────────►│ │
│ Thin Add-In (.NET Fx │ │ Business Logic │
│ or .NET 8 comhost) │ │ Cloud Services │
│ │ │ Modern UI (WPF/WinUI)│
│ - Handles SW API calls │ │ Database Access │
│ - Batches operations │ │ ML/AI Integration │
└─────────────────────────┘ └──────────────────────┘
The critical performance consideration: out-of-process SolidWorks API calls are 92x to 1000x slower than in-process calls. A GetBodies2 call that takes 2 milliseconds in-process can take 200 milliseconds across process boundaries due to COM marshaling overhead.
Mitigation strategy: Keep all SolidWorks API calls in the in-process add-in. Use an idle-time queue pattern where the out-of-process app sends operation requests, and the in-process add-in executes them in batches during OnIdle events. This brings performance within 1.5x of pure in-process — still a penalty, but manageable.
This approach works well when:
- Your add-in does heavy computation (FEA, optimization, ML inference) that doesn’t require constant API access
- You need a modern UI framework (WinUI 3, Blazor Hybrid) that won’t load in SolidWorks’ process
- You’re integrating with cloud services, databases, or other modern .NET ecosystems
Approach 3: NativeAOT for Computation Libraries
If you have performance-critical code that doesn’t need SolidWorks API access at runtime (geometry calculations, file parsing, data transformation), you can compile it with NativeAOT to produce a native DLL:
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<PublishAot>true</PublishAot>
</PropertyGroup>
Expose functions via [UnmanagedCallersOnly]:
[UnmanagedCallersOnly(EntryPoint = "CalculateUnfoldLength")]
public static double CalculateUnfoldLength(double radius, double angle, double kFactor)
{
// Modern C# code compiled to native
return Math.PI * (radius + kFactor * thickness) * angle / 180.0;
}
Your .NET Framework add-in can then P/Invoke these functions with zero marshaling overhead. This is ideal for computation-heavy operations like flat pattern calculations where you want modern .NET performance without touching the COM boundary.
Limitations: You can’t implement COM interfaces (like ISwAddin) via NativeAOT. This is strictly for helper libraries, not the add-in entry point itself.
What Approach Should You Choose?
| Scenario | Recommended Approach |
|---|---|
| New add-in, want modern .NET | Approach 1: EnableComHosting with .NET 8 |
| Existing .NET Framework add-in, gradual migration | Approach 2: Out-of-process for new features |
| Heavy computation (FEA, optimization) | Approach 3: NativeAOT for computation + Approach 1 or 2 for the add-in shell |
| Need to support multiple SW versions | Approach 1 with xCAD.NET, which handles version differences |
| Enterprise deployment (no admin rights) | Approach 2: Out-of-process components need no COM registration |
Same Task, Two APIs: Open File and Export Flat Pattern DXF
The architectural differences become concrete when you write the same operation in both APIs. Here’s opening a sheet metal part and exporting its flat pattern to DXF — a bread-and-butter task for any CadShift-style automation tool.
SolidWorks COM add-in (.NET Framework 4.x):
// Must be called on the SolidWorks UI thread (in-process add-in)
int errors = 0, warnings = 0;
ModelDoc2 doc = (ModelDoc2)swApp.OpenDoc6(
@"C:\parts\bracket.sldprt",
(int)swDocumentTypes_e.swDocPART,
(int)swOpenDocOptions_e.swOpenDocOptions_Silent,
"",
ref errors,
ref warnings);
// Find the sheet metal body
PartDoc part = (PartDoc)doc;
object[] bodies = (object[])part.GetBodies2(
(int)swBodyType_e.swSolidBody, false);
Body2 flatBody = null;
foreach (Body2 b in bodies.Cast<Body2>())
if (b.IsSheetMetal()) { flatBody = b; break; }
// Export flat pattern — note the bitmask for entity selection
string dxfPath = @"C:\output\bracket.dxf";
int options = (int)swExportFlatPatternViewOptions_e
.swExportFlatPatternOption_ExteriorFaceVisible
| (int)swExportFlatPatternViewOptions_e
.swExportFlatPatternOption_BendLines;
bool ok = doc.Extension.ExportFlatPatternView(
dxfPath, options, true, flatBody);
swApp.CloseDoc(doc.GetPathName());
Inventor C# add-in (.NET 8 via ClrAddinLoader):
// Inventor's API uses pure .NET — no ref parameters, no int casts
Document doc = inventorApp.Documents.Open(
@"C:\parts\bracket.ipt",
openVisible: false); // headless, no window created
SheetMetalComponentDefinition def =
(SheetMetalComponentDefinition)
((PartDocument)doc).ComponentDefinition;
// DataIO string identifies the export type — no enum required
string dxfPath = @"C:\output\bracket.dxf";
def.DataIO.WriteDataToFile("FlatPattern DXF", dxfPath);
doc.Close(saveDocument: false);
The difference in ceremony is significant. SolidWorks requires ref parameters, integer casts from enums, manual body traversal, and a separate close call with the path string (not the document reference). Inventor’s API is a clean .NET object model: open, navigate, write, close. Both produce the same output file, but the Inventor version compiles on .NET 8 without modification, uses no COM interop attributes, and requires no regasm to deploy.
The SolidWorks version requires admin rights for registration, is locked to .NET Framework 4.x, and must run on the SolidWorks UI thread. The Inventor version can run on any thread, targets .NET 8, and deploys by dropping files in a folder.
Frequently Asked Questions
Can I use .NET 8 in a SolidWorks add-in today?
Yes, via EnableComHosting in your .csproj. The build produces a comhost.dll alongside your assembly. Register it with regsvr32 MyAddin.comhost.dll instead of running regasm. Your managed code runs on .NET 8; the COM interface SolidWorks sees is unchanged. The [ComVisible], [Guid], and [ComRegisterFunction] attributes are still required. xCAD.NET wraps this registration pattern if you want a framework that handles the boilerplate.
Do I still need admin rights with EnableComHosting?
Yes. regsvr32 writes to HKCR\CLSID in HKEY_LOCAL_MACHINE just like regasm. Per-user COM registration (regsvr32 /n /i:user) exists but SolidWorks doesn’t discover per-user COM servers — it only reads HKLM\SOFTWARE\SolidWorks\AddIns. Admin rights remain a deployment requirement as long as SolidWorks uses registry-based discovery.
What if two add-ins both use EnableComHosting but target different .NET versions?
You get a runtime conflict. Only one .NET Core runtime version can be hosted per process. If your add-in loads .NET 8 and another add-in tries to load .NET 9 (or .NET 7), the second load fails with a CLR_E_SHIM_RUNTIMELOAD error. Both add-ins must target the same major .NET version, or one must move to the out-of-process architecture where each process hosts its own runtime independently.
Why can’t SolidWorks just load add-ins from a folder like Inventor does?
SolidWorks delegates discovery to the Windows COM runtime — CoCreateInstance reads from HKCR\CLSID. Changing to folder-based discovery would require replacing the loading logic in sldappu.dll’s CSwAddinManager with a purpose-built loader like Inventor’s RxAddIns.dll + ClrAddinLoader.dll stack. That’s a non-trivial change to a core system component, and it would break any add-in that registers via COM but not via the new folder path. Inventor built its loader from scratch before COM registration was entrenched in its ecosystem.
Does Inventor’s manifest approach support unmanaged (C++) add-ins?
Yes. The .addin manifest format covers both managed and native add-ins. For native DLLs, Inventor’s RxAddIns.dll calls DllGetClassObject directly — ClrAddinLoader is only invoked for managed assemblies. The manifest is the unified discovery mechanism; the loading path branches based on whether the assembly path resolves to a native or managed DLL.
Can an Inventor add-in call SolidWorks and vice versa?
Not directly. Both are in-process COM servers that expect their host application’s object model. SolidWorks add-ins get ISldWorks, Inventor add-ins get Application (Inventor’s). The out-of-process architecture (Approach 2) is the practical path for cross-platform tools: a standalone .NET 8 process drives both APIs via their standalone/remote COM interfaces, calling each application’s ISldWorks/Application object through a running instance.
What SolidWorks Could Learn from Inventor
Inventor’s approach isn’t technically revolutionary. The .addin manifest + ClrAddinLoader shim pattern is essentially what COM Registration-Free Manifests (Reg-Free COM) were supposed to be, but done right — with a purpose-built loader that understands .NET assembly loading instead of relying on Windows’ generic Side-by-Side (SxS) infrastructure.
The key engineering decisions Inventor made:
Own the loading pipeline. Instead of delegating to
CoCreateInstance, Inventor’sRxAddIns.dllcallsClrAddinLoaderdirectly. This gives them control over which runtime loads, how assemblies are resolved, and how types are discovered.Ship dual loaders. By including both
ClrAddinLoader.dll(.NET 8) andClrAddinLoader48.dll(.NET Framework 4.8), Inventor supports both modern and legacy add-ins without forcing a migration. Developers can upgrade on their own timeline.Use reflection, not registry. The
ClrAddinLoaderscansassembly.GetExportedTypes()for types implementingApplicationAddInServerand matches them by GUID against the manifest. No global COM registration needed.Declarative metadata. Load behavior, version constraints, localization, and translator capabilities are all in the manifest XML. SolidWorks requires developers to implement these features programmatically.
SolidWorks could implement a similar system without breaking backward compatibility. A hypothetical SolidWorks\Addins\ directory with .swaddin manifest files, processed by a managed loader DLL, would let new add-ins use modern .NET while existing COM-registered add-ins continue to work through the registry path.
Until that happens, EnableComHosting with a comhost.dll is the most practical path forward. It’s not as clean as Inventor’s manifest approach — you still need COM registration and admin rights — but it breaks the .NET Framework lock-in that has held back SolidWorks add-in development for over a decade.
Takeaways
SolidWorks loads add-ins via
CoCreateInstance— standard Windows COM activation that requires registry entries underHKLM\SOFTWARE\SolidWorks\AddIns\andHKCR\CLSID\. Themscoree.dllCLR shim locks add-ins to .NET Framework 4.x.Inventor uses
.addinXML manifests parsed by its nativeRxAddIns.dll, which delegates toClrAddinLoader.dll— a custom COM class factory that loads assemblies via reflection. No global COM registration, noregasm, no admin rights for user add-ins.Inventor ships dual .NET loaders:
ClrAddinLoader.dllfor .NET 8 andClrAddinLoader48.dllfor .NET Framework 4.8. Both exposeDllSetClrAssemblyFileSpec+DllGetClassObject— the same shim pattern, targeting different runtimes.The best modern .NET option for SolidWorks today is
EnableComHostingin a .NET 8 project, which produces acomhost.dllregistered viaregsvr32. This replaces themscoree.dllshim with a CoreCLR host while maintaining COM compatibility.For heavy computation, NativeAOT compilation lets you write .NET 8 code that compiles to a native DLL callable via P/Invoke — zero runtime overhead, usable from any .NET Framework add-in.
The four levels of CAD automation — macro, standalone, add-in, platform — each come with different constraints. Understanding the COM boundary at the add-in level is essential for choosing between in-process and standalone architectures.
WPF controls in SolidWorks add-ins introduce additional assembly resolution complexity at the .NET/COM boundary. The host process’s
ApplicationBaseisC:\Program Files\SOLIDWORKS Corp\SOLIDWORKS\, so WPF satellite assemblies and third-party dependencies must be discoverable from that root or resolved via a customAssemblyResolvehandler. See resolving WPF and assembly loading issues in SolidWorks COM add-ins for the specific failure modes and diagnostic checklist.