views:

123

answers:

2

An interesting question arose today. Let's say I have a .NET object that implements a certain interface IMyInterface and is also COM Visible.

Now, I load the type from its ProgID and cast to a strongly-typed interface, like so:

IMyInterface objEarlyBound = null;
Type t = Type.GetTypeFromProgID("My.ProgId");
objLateBound = Activator.CreateInstance(t);

objEarlyBound= (IMyInterface)objLateBound;

objEarlyBound.Method();

If I execute the above code, when objEarlyBound.Method() executes, am I calling into a COM object or directly into the .NET object? How can I prove this one way or another?

+1  A: 

You can't prove much either way - it's an internal implementation choice that has been deliberately concealed from you.

objEarlyBound and objLateBound are required to have the same identity, that is == on them will return true. So from each of them you can always obtain the other, and ditto for any other interfaces they support, and also if you assign either of them to object. But that doesn't prove anything much.

If you directly referenced the assembly containing the class that is exposed through COM as My.ProgId, you could say:

IMyInterface objEarlyBound = new MyClassExposedThroughCOM();

At this point the CLR's COM support has not been required at all. But then you could pass that object to some external COM library, and at that point the CLR would create a CCW to associate with the object. And having done that once, it can always get back to the same CCW for any given CLR object.

So to relate that to your example, you start with a RCW around a COM object, you cast it to an interface, and at that point the CLR could (for all we know) query for a special internal COM interface that, if found, allows it to get hold of the internal CLR object, and thus bypass COM completely from then on. If at any time it needs to get back to the CCW, it can do so, as it has to be able to do that at any time when working in the other direction.

I've tried putting a breakpoint in a hand-implemented C++ COM object's QueryInterface function, to see what the CLR queries for. Basically it tries a lot of things, some of which I couldn't immediately identify, so it could well be "sniffing" for a fellow CLR object. It makes sense for it to do that, to avoid a crazy COM sandwich situation, in which a CLR reference points to a RCW, which points to a CCW, which points to a CLR object. This can clearly be simplified by having the CLR reference just point directly to the CLR object.

Actually now I think about it, it doesn't need to query to find this out: it has a global table of CCWs that it has previously generated, so it can just look up any new IUnknown in there. COM objects are required to always return the exact same address for IUnknown so it can be used for object identity comparison. Hence the CLR can always recognise a COM object that it is implementing itself, and get the corresponding CLR object.

By the way, all of this discussion assumes that the COM object is in-process. If it is out-of-process then the situation is totally different; each process has its own CLR instance and so may as well use the COM implementation of interprocess marshalling.

Daniel Earwicker
That's interesting and helpful, thanks. Can I stand that on its head ad say, if I take objLateBound from my example above, and call into it using objLateBound.InvokeMember("Method", ...) how does that affect things? Would that always be a call through the CCW, or do you still think the CLR might be smart enough to sniff the .NET class and use it directly?
Tim Long
+2  A: 

RCW's should be (from what I know) of the special type System.__ComObject, however probably what you want to do is call Marshal.IsComObject which will tell you if the object is a runtime wrapper or not. In a quick test a COM created object via that route ends up as a direct managed object and loses the COM wrapper.

tyranid