none
Exactly when does a runtime callable wrapper call QueryInterface?

    Question

  • Hello all,

    we are using a COM component written in ATL that answers queries for interfaces depending on the capabilities of connected hardware. Either it returns E_NOINTERFACE or the corresponding interface. This is implementied with the following entries in the COM MAP:

    BEGIN_COM_MAP(CController)

    ...

    COM_INTERFACE_ENTRY_FUNC(__uuidof(IOptional), 0, &QueryOptionalInteraface)

    END_COM_MAP()

    We are currently writing a C# client for this component. The code looks something like this:

    ControllerClass controller = new ControllerClass();

    IOptional optional = controller as IOptional;

    if (optional != null)

    {        // shouldn't come here!

    Console.WriteLine(optional.Name);      // invalid cast exception thrown here!

    }

    Now, in the case QueryOptionalInterface returns E_NOINTERFACE (i.e. CController.QueryInterface(__uuidof(IOptional)) returns E_NOINTERFACE, we still receive a managed IOptional interface (optional != null). Later, when we access the first property of the interace, an exception is thrown.

    Of course we can catch the exception, but this seems to be not very elegant.

    I always thought, that a cast (explicit or via as or is operators) directly triggers a QueryInterface on the COM object. But this seems not to be the case (we verified this be debugging the unmanaged code).

    Is there a way around this, apart from catching exceptions?

    By the way, we cannot change the COM object, because this would break existing unmanaged clients.

    Best regards,

    Bernd

     

    Tuesday, April 04, 2006 1:40 PM

Answers

  • In Ioptional optional = controller as optional;

    I was expecting 'optional' to be null.

    This is because the as operator doesn't throw an exception when it fails but instead fills the variable with null. The check if the value was null checked if the code worked or not. I am surprised that it didnt work for you.

    It should throw a Structured Exception if I do the following

    IOptional optional = (IOptional)controller; // If I dont implement IOptional this should throw an InvalidCastException.

    The funny thing is that while all documentation suggests that QueryInterface is called on a cast, debugging unmanaged code does seem to suggest that its acutally called on the first method call!! All managed objects seem to be lazy proxies! Well that MickeySoft for you.

    I would try-catch the entire block, if I were you.

    My colleague reckons:

    "Casting on RCW-based objects is handled differently from normal mananged objects.  For interface casting, the CLR first checks the metadata for a ‘static’ interface match…that is, that the interface being cast to is already listed in the metadata from the interop assembly.  QI will be called in cast only if this fails!"

    This is interesting as we could have no component registered but still have successful casts on interop objects!!

    Thursday, April 06, 2006 4:25 PM

All replies

  • In Ioptional optional = controller as optional;

    I was expecting 'optional' to be null.

    This is because the as operator doesn't throw an exception when it fails but instead fills the variable with null. The check if the value was null checked if the code worked or not. I am surprised that it didnt work for you.

    It should throw a Structured Exception if I do the following

    IOptional optional = (IOptional)controller; // If I dont implement IOptional this should throw an InvalidCastException.

    The funny thing is that while all documentation suggests that QueryInterface is called on a cast, debugging unmanaged code does seem to suggest that its acutally called on the first method call!! All managed objects seem to be lazy proxies! Well that MickeySoft for you.

    I would try-catch the entire block, if I were you.

    My colleague reckons:

    "Casting on RCW-based objects is handled differently from normal mananged objects.  For interface casting, the CLR first checks the metadata for a ‘static’ interface match…that is, that the interface being cast to is already listed in the metadata from the interop assembly.  QI will be called in cast only if this fails!"

    This is interesting as we could have no component registered but still have successful casts on interop objects!!

    Thursday, April 06, 2006 4:25 PM
  •  

    Nikhil,

    thanks for the reply. I think that an auto-generated RCW implements all the interfaces that the corresponding COM coclass lists in it's IDL.

    Therefore a cast (either via (IOptional) or as or is operators) works regardless of the underlying QueryInterface implementation. In this sense these are managed casts on the managed RCW object implementing the managed interfaces and this works in any case.

    Only at the time of an access to a property or method of the corresponding interface the RCW finally calls the QueryInterface, which still might fail at this point.

    So this is really a thing one should keep in mind.

    We will use the suggested try/catch solution, because I currently see no other.

    Best regards,

    Bernd

     

     

    Friday, April 07, 2006 12:13 PM