JC,
When you pass a SAFEARRAY from unmanaged code to managed code, what
happens as far as garbage collection depends on the types that are in the
safearray.
First off, the safearray is always released.
Before it is released, the runtime is going to marshal the elements from
the safearray from the unmanaged realm over to the managed realm. If the
safearray contains primitive types, then those are marshaled across into the
new managed array. If the safearray contains interface pointers, then those
are marshaled across, and the reference count is incremented by one on each
of them, assuring that they are alive when accessed by managed code. Then,
when the SAFEARRAY is deallocated (when the marshaling is complete), the
reference count will be reduced by one (which is fine, since the initial
entry of the item into the safe array should have incremented the reference
count by one), resulting in a net change of plus one for the reference
count, that reference being held by the managed wrapper for the COM object.
Now, these managed representations are subject to the same rules as any
other managed object, and will be garbage collected if they can not be
reached. When GCed, the reference count will be decremented by one, unless
a call to ReleaseComObject is made on the Marshal class, passing the COM
object. This will reduce the reference count by one at that point, which is
good practice, releasing it when you need it.
As for going the other way, when marshaling a .NET object into the
unmanaged realm, the runtime keeps a reference to it. When the COM-callable
wrapper is released (the reference count is set to zero), then the runtime
releases the reference, and it is subject to garbage collection, assuming
there are no other references.
Hope this helps.