COM Interop Best Practices for .NET Developers
Essential patterns for reliable COM automation from C# — proper object cleanup, STA threading, the two-dot rule, and avoiding memory leaks when automating SolidWorks, Excel, and other COM servers.
Why COM Interop Is Tricky
COM (Component Object Model) is the foundation of Windows automation. SolidWorks, Excel, AutoCAD, and hundreds of other applications expose their APIs through COM. From C#, you interact with COM objects through Runtime Callable Wrappers (RCWs) — .NET proxies that manage the COM reference counting for you.
The problem: .NET's garbage collector and COM's reference counting model don't always agree on when to release objects. This leads to memory leaks, zombie processes, and mysterious crashes.
Rule 1: STA Threading
Most COM servers (including SolidWorks and Excel) require Single-Threaded Apartment (STA) threading:
[STAThread]
static void Main(string[] args)
{
// Your COM automation code here
}For WPF apps, this is automatic. For console apps and services, you must set it explicitly. Calling COM methods from an MTA thread causes marshaling overhead at best and deadlocks at worst.
Rule 2: The Two-Dot Rule
Never chain more than one dot when accessing COM properties:
// BAD — creates a hidden intermediate RCW that you can't release
string name = model.Extension.CustomPropertyManager[""].Get(...);
// GOOD — assign each intermediate object to a variable
var ext = model.Extension;
var mgr = ext.CustomPropertyManager[""];
string name = mgr.Get(...);
Marshal.ReleaseComObject(mgr);
Marshal.ReleaseComObject(ext);Each dot access may create a new RCW. If you don't hold a reference to it, you can't release it, and the COM object stays alive.
Rule 3: Release COM Objects Deterministically
Don't rely on the garbage collector to release COM objects — it's not timely enough:
IEdmVault5 vault = new EdmVault5();
try
{
vault.LoginAuto("MyVault", 0);
// ... work with vault ...
}
finally
{
Marshal.ReleaseComObject(vault);
}For loops, release objects inside the loop body to prevent accumulation:
foreach (var file in files)
{
var doc = swApp.OpenDoc6(file, ...);
try { /* process */ }
finally { swApp.CloseDoc(doc.GetTitle()); }
}Rule 4: Handle Arrays from COM
COM methods often return variant arrays. In C#, these arrive as object or object[]:
// SolidWorks returns object[] from GetChildren()
var children = (object[])component.GetChildren();
foreach (Component2 child in children)
{
// Process child
}Always check for null before casting — many COM methods return null instead of an empty array when there are no results.
Rule 5: .NET Framework vs .NET 5+
- .NET Framework: Full COM support out of the box. Primary Interop Assemblies (PIAs) work natively. This is still required for SolidWorks PDM add-ins
- .NET 5+: COM Interop works but some features (like hosting COM add-ins) are limited. Use
System.Runtime.InteropServicesand type-embed the interop assemblies - When in doubt: Use .NET Framework for COM-heavy automation projects. The compatibility is simply better
Common Patterns
Safe COM Wrapper
public static T? TryCom<T>(Func<T> action) where T : class
{
try { return action(); }
catch (COMException) { return null; }
}Batch Processing with Cleanup
public void ProcessFiles(SldWorks swApp, string[] paths)
{
foreach (var path in paths)
{
int err = 0, warn = 0;
ModelDoc2? doc = null;
try
{
doc = (ModelDoc2)swApp.OpenDoc6(path, ...);
if (doc == null) continue;
// Process document
}
finally
{
if (doc != null)
{
swApp.CloseDoc(doc.GetTitle());
Marshal.ReleaseComObject(doc);
}
}
}
}Tips
- Use Task Manager to verify your COM server (SolidWorks.exe, EXCEL.EXE) actually closes after your program finishes — lingering processes indicate a reference leak
- GC.Collect() + GC.WaitForPendingFinalizers() after releasing COM objects can help in stubborn cases, but it's a last resort — prefer explicit ReleaseComObject calls
- If automating Excel, set Application.Visible = false and Application.DisplayAlerts = false for headless operation