Search code examples
c#.netcominterop

Why doesn't Marshal.Release call Release on my COM object?


I've read the documentation for Marshal.GetIUnknownForObject and it says:

Always use Marshal.Release to decrement the reference count once you have finished with the pointer.

I wrote a test solution with a COM object which I use via .NET and I see that when working with the wrapper to my COM object neither Marshal.GetIUnknownForObject nor Marshal.Release cause the AddRef or Release methods of my COM object to be called.

What's up with that?


Solution

  • Wait a minute, I think I've got it. You are making a COM object, then you are making a runtime callable wrapper around that COM object, and then you're asking the runtime callable wrapper for its punk, and then you're wondering why calling release on that punk doesn't call your Release? Does that sum up your question?

    If so, you might have mentioned all those details in the question rather than making us guess.

    Well, why do you suppose it would it call your Release? it's going to call the Release of the runtime callable wrapper. The wrapper wraps your object; that's why its called a wrapper. The runtime callable wrapper doesn't need to addref and release your object every time it is addref'd and released! Why would it? It needs to keep your object alive and it already has a ref on your object; that is sufficient to keep the wrapped object alive. Why do unnecessary work?

    When you just have a runtime callable wrapper around a COM object you should reasonably expect to that the ref count on the object is unchanging for the entire lifetime of the object. The runtime takes a ref once, and when the RCW is garbage collected for the last time or released in some other manner, it releases that ref and the object deletes itself.

    Think about it like this. Suppose you said:

    foo->bar = blah;
    blah->AddRef();
    

    blah gets addref'd because foo->bar has a reference to it. Now you say:

    abc->def = foo;
    foo->AddRef();
    

    That adds a ref to foo. Does it add a ref to blah? Of course not. Why would it? foo has a ref to blah; the fact that foo is being kept alive by abc is irrelevant to blah.

    Same thing here. You have:

    wrapper = new RCW();
    wrapper->wrapped = yourobject;
    yourobject->AddRef();
    

    That adds a ref to yourobject. Now if you have

    wrapper->QI(IUnknown, &punk)
    

    that doesn't give you the punk to the underlying object, it gives you the punk of the wrapper. If you say

    punk->AddRef();
    

    that doesn't addref the underlying object, it addrefs the wrapper.