It’s impressive how hard it is to use VSTO correctly.
When a COM handle crosses from unmanaged into managed code for the first time, a “runtime callable wrapper” is created for it. This wrapper maintains a reference count, incremented whenever the object is newly requested across the boundary.
The wrapper releases the COM object behind it when its finalizer is run, but this relies on all .NET references to the RCW itself becoming inaccessible to the GC and for the GC to pick it up. This may not reliably happen.
You can force the matter by decrementing the reference count on the RCW yourself: Marshal.ReleaseComObject does this, and should the reference count drop to zero, the COM object will get released immediately. This means taking a lot of care, though, since it’s not always obvious which VSTO calls result in the counter being incremented and which don’t. Something like the following can help work it out:
If each call to Marshal.ReleaseComObject returns the same number, it implies each call is incrementing the reference count; if the return values are decreasing, the call is not incrementing the count.
VSTO event callbacks are said to not need references released on passed-in COM objects, but an MSDN blog disagrees. There is no consensus here. For anything else at least: remember to release COM objects when you’re done with them, otherwise it can get painful.