From: Igor R. on 5 May 2010 04:20 > But, let's assume that the object returned by "SDKLib.Child()" is a COM object wrapped in RCW. Right. I deal with COM-objects in C#, so all the entities in my example are COM-objects as they're seen from within .NET (i.e. RCWs). > Let's further assume that "someParent.setChild(child)" for whatever reason does _not_ retain a reference to the RCW object that was returned by "SDKLib.Child()" > (perhaps it itself is an RCW, and thus the only thing it internally is retaining is the unmanaged COM interface reference). Exaclty. > If all that's true, then yes without any references to the wrapper object, it will be GC'ed, and the COM object released when that happens. The COM object is not released, as it's referenced internally by "someParent" (i.e. by its underlying COM object) after I apply setChild(). But the wrapping RCW is really GC-ed. > As for the solution, it's the same as if you had a managed object with an event that you subscribed to. You have to keep a reference to it, otherwise the object with the event will be collected. Why should the RCW be any different? Ok, I see... That's what I'm doing. I just thought that maybe there exists some way to reference the RCW *inside* setChild() method. I.e. I'd like to cause the following expression to increase child's RCW reference count: aParent.setChild(child) -- where aParent itself is a RCW of a COM object and setChild is COM method. By the way, what if "child" were a regular C# object exposing a COM interface? IIUC, then setChild() would receive its CCW, and the CCW would reference the underlying .NET object -- right? So it wouldn't get GC-ed! What I want is to get a similar effect with RCW. What I don't quite understand is what happens exactly when I pass "child" RCW to the setChild() method. Inside setChild, do I still have an access to the "child" RCW, or it's "unwrapped", and the "naked" COM object is passed as a parameter?
From: Peter Duniho on 5 May 2010 11:28 Igor R. wrote: > [...] > The COM object is not released, as it's referenced internally by > "someParent" (i.e. by its underlying COM object) after I apply > setChild(). But the wrapping RCW is really GC-ed. It's released by the RCW (as in, the RCW calls the IUnknown.Release() method). Just because its reference count didn't reach zero when it was released doesn't mean it wasn't released. > [...] > I just thought that maybe there exists some way to reference the RCW > *inside* setChild() method. I.e. I'd like to cause the following > expression to increase child's RCW reference count: > aParent.setChild(child) -- where aParent itself is a RCW of a COM > object and setChild is COM method. ..NET doesn't use reference counting. The RCW doesn't itself have a reference count to be increased. The COM object's reference count may be increased by the setChild() method itself, but that only affects the COM object itself, not the RCW that is wrapping it. > By the way, what if "child" were a regular C# object exposing a COM > interface? IIUC, then setChild() would receive its CCW, and the CCW > would reference the underlying .NET object -- right? Yes. Going the other way, with a managed implementation of a COM object referenced outside managed code, obviously the reference count from outside managed code must affect the lifetime of the managed implementation. > So it wouldn't > get GC-ed! What I want is to get a similar effect with RCW. The "similar effect" follows the same rules for any other managed object: you have to keep a reference to it somewhere. > What I don't quite understand is what happens exactly when I pass > "child" RCW to the setChild() method. Inside setChild, do I still have > an access to the "child" RCW, or it's "unwrapped", and the "naked" COM > object is passed as a parameter? The unmanaged COM object's setChild() method will see only the unmanaged COM object that was wrapped by the RCW. .NET's COM marshaling has "unwrapped" the object by that point in the call chain. It cannot be any other way; after all, what would unmanaged code expecting a plain, unmanaged COM interface instance do with a managed object reference? Pete
From: Igor R. on 5 May 2010 16:58 > It's released by the RCW (as in, the RCW calls the IUnknown.Release() > method). Just because its reference count didn't reach zero when it was > released doesn't mean it wasn't released. Ah, sorry, by saying "released" I meant "totally released", i.e. freed/ disposed. > The unmanaged COM object's setChild() method will see only the unmanaged > COM object that was wrapped by the RCW. .NET's COM marshaling has > "unwrapped" the object by that point in the call chain. It cannot be > any other way; after all, what would unmanaged code expecting a plain, > unmanaged COM interface instance do with a managed object reference? 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. And what we get now? 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. 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? 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!
From: Peter Duniho on 5 May 2010 21:31
Igor R. wrote: > [...] > 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. > And what we get now? 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 |