Search code examples
c++commarshallingatlcom+

Why would QueryInterface() fail when the interface is surely implemented and has built-in marshaller in Windows?


I have the following setup. There's a COM server that is installed into COM+ (to run in a separate process) and has this interface definition:

[object, uuid("InterfaceIdHere"), nonextensible, oleautomation, hidden]
interface IMyInterface : IUnknown {
   HRESULT MyMethod( [in] IUnknown* param );
};

The caller calls it like this:

HRESULT callComObject(IStream* stream)
{
    return comObject->MyMethod(stream);
}

Note that here IStream* is implicitly upcasted to IUnknown*. This is done because declaring a parameter of type IStream* in IDL caused some problems that I can't recall right now. Anyway it's always a valid IStream* that is passed in place of IUnknown*.

The COM server side has this implementation of MyMethod():

STDMETHODIMP CServer::MyMethod(IUnknown* param)
{
    if(param == 0) {
       return E_INVALIDARG;
    }   
    ATL::CComQIPtr<IStream> stream(param);
    if(stream == 0) {
       return E_INVALIDARG;// control passes HERE
    }
    // whatever
}

So I have IStream* passed into callComObject() on the client side which is implicitly upcasted to IUnknown* and the latter is passed to the COM marshaller. The marshalled IUnknown* reaches the server in another process and there IUnknown* is obtained and then there's a QueryInterface() call to marshall IStream* from the same object and that QueryInterface() fails.

This looks insane, because marshalling IStream* should just work at all times - there's a marshaller for this interface pre-installed in Windows.

Why could it possible not work and how do I find the reason?


Solution

  • One of the possible scenarios that matches the behavior is the following:

    • you don't have any marhshaling between caller and callee
    • the interface pointer is valid
    • however the object which implements IStream, does not have a corresponding COM_INTERFACE_ENTRY map entry and does not make the interface available, the caller might have obtained the pointer via non-COM way, e.g. direct C++ cast

    This is easy to check by QueryInterface'ing the stream on the caller side before the call.

    The callee in this scenario can just reinterpret_cast to IStream and have it nicely working.