I've run into an issue that I think is related to the way the CLR interops with COM objects, but I was hoping some folks here could maybe offer a little more insight. I want to apologize in advance for the vagueness of the question, unfortunately I'm integrating with a somewhat opaque system.
Consider the following code:
class Foo
{
private IComInterface comObject;
Foo(IServiceProvider provider)
{
this.comObject = serverProvider.GetService(typeof(ISomeService)) as IComInterface;
Debug.Assert(this.comObject != null); // comObject is *not* null here
}
void Bar()
{
IOtherComInterface otherInterface = this.comObject as IOtherComInterface;
Console.WriteLine(otherInterface == null);
}
}
The COM interop types are embedded in my assembly, which is loaded as a plugin by another program. When I first create an instance of Foo
, the COM object provided by the service provider (which is provided by the program) is non-null. However, when I immediately call Bar()
, the cast to IComOtherInterface
doesn't work: the method prints "true".
My problem, though, is that after some other plugins are loaded, calling Bar()
again prints "false". I've verified that it's the same instance of Foo
, and in fact the same instance of comObject
(I tagged both with IDs using the debugger and the numbers didn't change). So now the cast is succeeding.
So my question is this: how is this possible? Is it possible that the object stored in comObject
is actually wrapping a new native COM object the second time through the same RCW? Is it possible that loading other assemblies has somehow changed the type-identity of IOtherComInterface
so that the cast now works? Some other crazy possibility that I can't actually fathom?
COM objects "live" in their apartments, so do their proxies. Passing "raw" COM interface pointers across apartment boundaries (which might sometimes read "across thread boundaries") is a typical reason to have COM interface pointers still operational in terms of no access violations and otherwise crashes, but failing on the expected tasks, including failing to return another interface pointer via QueryInterface
.
Once you have a COM interface pointer, you are interested to use it in the COM apartment and respective threads, where you obtained the pointer on.
To pass pointers between apartments/threads, you need to take additional steps (marshal the pointer on original thread, and then unmarshal back on the target thread). See also: