C# – Absolute COM Confusion – C# interop with early-binding

ccomcom-interopinterop

I've been battling with VStudio, Google and various other tools and websites all day and found no solution – HELP!!

I have two COM interfaces (pure COM, no ATL):

IMyClassFactory and IMyClass with corresponding implementations

I want to use them from C# BUT without registering the COM server with regsvr32.
I expose the Class Factory with CoRegisterClassObject and I can successfully create objects of IMyClass with CoCreateInstance from unmanaged code.

So the C# interop…

I created a .NET wrapper with tlbimp myComServer.tlb and loaded it as a reference to my C# client.

Then, when I try to create an instance of IMyClass, I get:

An unhandled exception of type 'System.InvalidCastException' occurred in COMTestClient.exe

Additional information: Unable to cast COM object of type 'MyComServerLib.MyClass' to interface type 'MyComServerLib.IMyClass'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{9F8CBFDC-8117-4B9F-9BDC-12D2E6A92A06}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

Now, I have added traces to the QueryInterface and the only cases when I return E_NOINTERFACE are when it requests for any of Marshal-related interfaces or for IManagedObject.

How do I fix this??

EDIT: My IDL file:

import "unknwn.idl";

[
    object, 
    uuid(...), 
    nonextensible,
    pointer_default(unique)
]
interface IMyClass : IUnknown
{
    HRESULT(SetFirstNumber)(long nX1);

    HRESULT(SetSecondNumber)(long nX2);

    HRESULT(DoTheAddition)([out,retval] long *pBuffer);
};

[
    uuid(...)
]
library MyLib
{
    importlib("stdole2.tlb");

    [
        uuid(...)
    ]
    coclass IMyClassImpl
    {
        [default] interface IMyClass;
    };
}

Best Answer

You need to either allow your interface to be marshalled (i.e., by marking it as not "local" in the .idl file so that it ends up in the type library, and in the proxy/stub), or aggregate the free-threaded marshaller if you go that way.

To aggregate the FTM, I did something like this:

#define DECLARE_FTM() \
protected: CComPtr<IUnknown> _m_Marshal; \
DECLARE_GET_CONTROLLING_UNKNOWN() \
public: HRESULT FinalConstruct() \
{ return CoCreateFreeThreadedMarshaler(GetControllingUnknown(),&_m_Marshal); }

#define COM_INTERFACE_ENTRY_FTM() COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal,_m_Marshal.p)

Then, in your COM map:

BEGIN_COM_MAP(Blah)
    COM_INTERFACE_ENTRY(IBlah)
    COM_INTERFACE_ENTRY_FTM()
END_COM_MAP()
DECLARE_FTM()

I note that you aren't using ATL and the like - you would need to modify this so that your QueryInterface implementation returns the FTM pointer when IMarshal is queried for.

Note that aggregating the FTM is not something that can really be done lightly - it makes a number of unsafe assumptions that are not always valid. For instance, your class cannot make use of any interfaces which are not themselves free-threaded.

The other alternative is basically as @[Franci Penov] said, you need to ensure that your interface is able to be marshalled. The way I understand it, there is a standard marshaller which is able to marshal any interface in the type library, or you (that is the midl compiler does it more or less automatically) can make a proxy/stub dll (or merge the code for the proxy/stub into your own dll) which is able to marshal it for you.

This article here describes the process of building and registering the proxy/stub in more detail.

Related Topic