views:

85

answers:

4

Suppose my COM object implements two or more COM interfaces:

class CMyClass : public IPersistFile, public IPersistStream {
};

when implementing QueryInterface() I need to be able to return an IUnknown* pointer. Since both base interfaces are derived from IUnknown I cannot upcast implicitly - such upcast would be umbiguous. To upcast explicitly I need to use either of the two ways:

 if( iid == __uuidof( IUnknown ) ) {
     *ppv = static_cast<IPersistFile*>( this );
     static_cast<IPersistFile*>( this )->AddRef();
     return S_OK;
 }

or

 if( iid == __uuidof( IUnknown ) ) {
     *ppv = static_cast<IPersistStream*>( this );
     static_cast<IPersistStream*>( this )->AddRef();
     return S_OK;
 }

Looks like the only requirement is that whenever QI() is called on an object it returns the same pointer each time and I meet that requirement if I choose any of the casts and just stick to it.

Which upcast should I choose and why?

A: 

IUknown is unimplemented. You need to provide all the implementation of IUnknown. Thus, to QI IUknown, you return the this pointer.

AddRef, Release and QI are all implemented by you and not on the parent interface anyway so you have no issues just CALLING addref, no casting is required.

Goz
Nope, disallowed in MI scenarios as the result is not unique. EVery interface, when QI'd for IUnknown must return the same.
MSalters
Hmm Its been ages since I did COM but I never recall having this problem as you describe it. Maybe I did. I thought that it wouldn't matter because if an object is created as CMyClass whatever the interface you actually have, IPersistFile for example, the QI call will jump to CMyClass::QueryInterface ...
Goz
A: 

Usually in cases where you want to cast to IUnknown an object with multiple inheritence from IUnknown, you cast it to one of its interface then cast to IUnknown...

Mike Gleason jr Couturier
Okay, but which interface to choose for the first cast?
sharptooth
It doesn't matter, if I recall correclty, IUnknown has only one implementation down the hierarchy. The road you take to it, is irrelevant!
Mike Gleason jr Couturier
Like Mark Ransom said, just pick a convention or an Interface that will be lest susceptible to be removed from the object...This is the way Microsoft is doing it but I don't know if they have a convention.
Mike Gleason jr Couturier
@Mike: The path you take **is** relevant, because the compiler will generate multiple vtables even if they point to the same functions. The address returned will be different, try it and see. Microsoft had a very complete description of how objects were laid out in memory, but I can't find it on the web at the moment.
Mark Ransom
Ahhh vtables, I miss those pointers (I'm doing C# at the moment). I can't say "I forgot" on this one, thanks for pointing that out to me! Don't bother for the link, I have the "COM and ATL 3" book I can reference to..
Mike Gleason jr Couturier
+3  A: 

It doesn't matter which upcast you use, only that you use the same one always. I'd just pick a convention, such as always returning the first one declared in the inheritance list.

Mark Ransom
+3  A: 

Mark Ransom already gave the correct answer - any will do, as long as it's consistent - but picking the first one has one minor advantage. Due to layout rules, the IUnknown* of the first interface will point to the start of the object. Any other IUnknown* will point to subsequent vtable pointers elsewhere in the object. For debugging purposes, it's very useful to know where ano object begins in memory.

MSalters