Igor said:
[...]
Ok, I see, that's the way it works... Too bad!
But IMHO it *could* (should?) behave another way. After all, the whole
purpose of all the RCW/CCW machinery is to make the COM <--> .NET
interaction as transparent and seemless to the user as possible.
I'd say "as practical". But sure, it's there to help you.
Something that is entirely consistent with the rest of .NET.
Assume there're 2 COM classes: Class1 is from an
unmanaged COM server and Class2 is a .NET object - both exposing
*exactly* the same COM interface defined in the unmanaged SomeLib.
IMyInterface class1 = new SomeLib.Class1();
IMyInterface class2 = new Class2();
And an unmanaged Container COM class:
IContainer container = new SomeLib.Container();
Now, what a surprise:
container.add(class1); // class1 will be GC-ed soon
container.add(class2); // class2 will be alive!
IMHO, such a behaviour is not transparent and not intuitive. It
requires from the user to understand all the CCW/RCW machinery, and to
adjust his code accordingly.
I disagree. The behavior is entirely intuitive.
The whole point of a GC system is for the GC to clean up objects that,
in the specific context of the memory managed by the GC, are no longer
needed.
In your example above, there is nothing left referencing the managed
object for "class1". Thus, it's no longer needed and the GC can clean
it up. For "class2", there is still something left referencing the
managed object, and thus it _is_ still needed and does not get cleaned up.
But it could be done another way. If RCW is a managed wrapping object
that exposes the relevant interface (or pretends so), then why not to
wrap it with CCW, just like any other managed COM-object?
Because the RCW isn't there to provide access to a managed
implementation of the COM interface. It's there to provide managed
access to an _unmanaged_ implementation of the COM interface. The RCW
needs to exist only as long as there is managed code referring to it,
because the only reason the RCW exists at all is to provide a conduit
from managed code to the unmanaged interface.
Also, the idea of marshaling COM calls through TWO layers of wrapper
rather than one, just so you can have your RCW lifetimes match that of
the unmanaged object seems rather silly to me. That's a significant
performance hit, to accomplish something your own code really ought to
be doing itself anyway (and which it has to for managed objects that
provide the exact same kind of features anyway!)
If class1
would be wrapped with CCW *on the top* of RCW, when passing it to an
unmanaged code, the whole mess would be avoided!
IMHO, the only "mess" here is your expectation that a managed object
that is used only by your managed code should somehow be magically
preserved without your managed code explicitly referring to it.
Consider this code, containing nothing involving COM at all:
class A
{
public event EventHandler Event;
}
class B
{
void Method()
{
new A().Event += Handler;
}
void Handler(object sender, EventArgs e) { }
}
How long do you think the instance of A allocated in B.Method() should
live when B.Method() is called?
I know how long _I_ think it should live: no longer than the next
collection cycle.
This is the exact same scenario you are dealing with.
Pete