[COM] How to override IUnknown::QueryInterface()?

D619d95cddb1edb227f51ef539d15cdc
0
Nautilus 103 Aug 28, 2012 at 21:31

On the premise that I’m no expert of Component Object Model…

If I understand correctly, every single COM object must inherit from the IUnknown interface, directly or indirectly. IUnknown is the very base building block of everything COM. Period.

Then, IUnknown itself is a mere interface with no implementation.
Speaking strict C++ OOP, IUnknown exposes pure virtual methods which you must override as you inherit from it.

Here are IUnknown’s methods. Any interface inheriting from IUnknown must provide its own implementation for each:

  • ::QueryInterface()
  • ::AddRef()
  • ::Release()

Not only that.
Any *other* interface inheriting from something even remotely inherited from IUnknown, must also re-implement IUnknown’s 3 methods. And although it’s syntactically possible to manually invoke the method implementation from any ancestor interface (that is, forward a call to any ancestor interface), it is Bad COM Design that is going to bite you eventually (I’m just not told how).
The bottom line is that you do not forward. You do override.

With all that said, I am currently wondering at how to implement my QueryInterface().

If I am forbidden to forward the call to my ancestor’s QueryInterface() (to automagically support all its interfaces) then how do I get to enforce the support? My ancestor interface isn’t written by me - and I can’t look at its source. I can’t guess by trial and error. That’s no serious approach.

How do I discover which interfaces my ancestor supports, then?

4 Replies

Please log in or register to post a reply.

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Aug 28, 2012 at 22:03

Hmm. I’ve never tried to implement my own COM object, but it seems bizarre to me that the refcounting behavior needs to be reimplemented by everything. You’d think that only the base class would implement it and then you’d have a virtual destructor to ensure derived classes get cleaned up when the object deallocs itself. Do the MS docs really say that every client must reimplement its own refcounting?

With the QueryInterface call in particular, I can’t think of any sane way to do that other than checking if it’s one of your own interfaces, handling that if so, and passing responsibility to the superclass otherwise.

D619d95cddb1edb227f51ef539d15cdc
0
Nautilus 103 Aug 28, 2012 at 22:57

I’ve been reading from different sources.
But here is a MSDN page to start from: IUnknown and Interface Inheritance. It doesn’t confirm everything I said, though. But it says one other I forgot to mention: looks like that inheriting from an interface already inherited from IUnknown is out of standard in COM. You can do it, but it’s as if COM wasn’t conceived for that.
I wonder, since I really have no choice but to use some call forwarding, would it be all that bad to reuse my ancestor’s QueryInterface() and then expand on it to cover the new functionality I added?
I don’t get why it would be bad design.

At first that MSDN page sounds foggy where it says:

All COM objects must implement the IUnknown interface because it provides the means, using QueryInterface, to move freely between the different interfaces that an object supports

It’s not foggy. It’s to be taken to the letter.
I thought it meant to “move between the interfaces found along the chain of inheritance”. Much like object slicing in OOP. I was wrong. An interface may support any interface, not only itself, its ancestors and its known descendants.

D619d95cddb1edb227f51ef539d15cdc
0
Nautilus 103 Aug 29, 2012 at 17:55

Here’s an interesting snippet of code from codeproject.com:
How to find the name for interfaces implemented by a COM object.

Haven’t tested it. But basically you search in the Registry for the CLSID registered for a given COM interface.
Those CLSID should match the IID constants you’d feed to QueryInterface().
However someone argued that CLSID are not quite the same as IID -if we exclude their typedef- and the article’s author never replied, leaving me wondering.

On a side note I found another foggy bit of documentation in the MSDN about QueryInterface(). I didn’t quite notice this before.
Much to my embarrasment I’m having difficulties to translate it into something un-ambiguos for me.
Here I quote from the MSDN page IUnknown::QueryInterface method:

Remarks
For any one object, a specific query for the IUnknown interface on any of the object’s interfaces must always return the same pointer value. This enables a client to determine whether two pointers point to the same component by calling QueryInterface with IID_IUnknown and comparing the results. It is specifically not the case that queries for interfaces other than IUnknown (even the same interface through the same pointer) must return the same pointer value.

It’s the first sentence. What do they mean by a query for the IUnknown interface on any of the object’s interfaces ?
If all COM objects are born to be just the typical IMyInterface that ihnerits from IUnknown, and nothing else is supposed to be involved, then “all the object’s interfaces” would be just 2: your IMyInterface and their IUnknown.
Now, to query IUnknown for a pointer to IUnknown of course will return an IUnknown*. And to query IMyInterface for an IUnknown pointer should just return a IMyInterface* sliced to an IUnknown*. Again I see nothing mental in it.
Then why the need to specify the thing in that convoluted way?

Last note is for the IUnknown MSDN page.
The When to implement paragraph states that each interface in a given object must provide its own implementation for IUnknown’s methods. I quote:

If you are using nested classes to implement multiple interfaces, you must implement IUnknown once for each interface you implement.

Funny how this last bit of info hints that a COM object can be more complex than just a 2-classer IMyInterface::IUnknown.
If I get my hands on the guy responsible to write the docs…

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Aug 29, 2012 at 18:48

I think a COM object can implement an arbitrary number of interfaces, however each interface may be a derivate of another interface - in fact, each interface must be a derivate of IUnknown. If I were to write it using standard C++ classes, I might have something like

// Public and visible to the outside world
class IUnknown { /* the usual methods, pure virtual */ };
class ICustomInterface1 : public IUnknown { /* some more pure virtual methods */ };
class ICustomInterface2 : public IUnknown { /* some more pure virtual methods */ };
class ICustomInterface2a : public ICustomInterface2 { /* some more pure virtual methods */ };

// Hidden in the implementation, not exposed
class TheImplementation : public IUnknown, public ICustomInterface1, public ICustomInterface2, public ICustomInterface2a
{
    // implementation of IUnknown methods
    // implementation of ICustomInterface1 methods (including its own copy of the IUnknown methods)
    // implementation of ICustomInterface2 methods (including its own copy of the IUnknown methods)
    // implementation of ICustomInterface2a methods (including its own copy of the ICustomInterface2 methods, including its own copy of IUnknown)
};

At least, that’s what I *think* they’re saying. I could be wrong. Of course, where duplicate methods exist (e.g. the four (!) copies of IUnknown), you could just write the actual code once and forward all the other copies to the copy you wrote.

This problem - getting multiple copies of a base class when you inherit it through some intermediate classes - is a classic gotcha of multiple inheritance, and in C++ you can use “virtual base classes” to collapse all the copies together into one. But it seems likely that COM doesn’t support that.