Re: CComPtr strange behaviour
Alexander Lamaison <awl03@doc.ic.ac.uk> wrote:
On Mon, 27 Jul 2009 08:56:31 -0400, Igor Tandetnik wrote:
What happens to the pointer _after_ folder_item_object returns? I bet
the caller just casts it to the expected interface type, without
using QI.
Ah, indeed:
CFolder::GetUIObjectOf(..., REFIID riid, void** ppv) {
CComPtr<IUnknown> object;
object = folder_item_object(..., riid);
*ppv = object.Detach();
}
GetUIObjectOf takes an IUnknown* pointer it got from
folder_item_object, and tells the caller that it's really a pointer
of type riid (which amounts to a downcast). Due to a happy
coincidence of implementation details, this does happen to be the
case when you use ".p" form in folder_item_object, but not when you
use assignment without ".p".
Aha! I think I see what you mean: so you're saying my implementation
of GetUIObjectOf has basically lied to the caller? The caller said
"hey CFolder, pass me a pointer to some object where the _pointer_ is
of type riid" in other words it wants a guarantee that it is safe to
cast the void** pointer without a QI? And I've lied to it by passing
a pointer of type IUnknown.
Correct. Now, it's possible that the problem is not with
CFolder::GetUIObjectOf, but with folder_item_object. There might be an
unspoken contract, which GetUIObjectOf relies on, that
folder_item_object returns not just any IUnknown, but specifically an
IUnknown that is an upcast from the interface identified by riid (so
that the correct pointer can be obtained with a downcast). In this case,
the non-".p" version of folder_item_object violates that contract. It's
hard to decide who to blame, since you are not using common COM
argument-passing conventions (which imply a well-known set of rules),
nor documenting your contracts explicitly.
It is precisely to avoid this kind of misunderstanding that methods like
QueryInterface or GetUIObjectOf define their [out] parameters as void**,
and not IUnknown**.
If my understanding is correct, this is the proper way to do it:
CFolder::GetUIObjectOf(..., REFIID riid, void** ppv) {
CComPtr<IUnknown> object;
object = folder_item_object(..., riid);
return object->QueryInterface(riid, ppv);
}
Is that right?
Well, that should work. Whether it's "right" is for you to decide.
This highlights to me the absence of type safety in a
void** out-param. Out of curiosity, why do these functions ask for
void** rather than IUnknown**?
How is that more type-safe? If the caller does want to get
IExtractIcon*, how would it get it from that IUnknown* pointer? If by a
downcast, then there goes your type safety. If by QueryInterface, then
what parameters should QueryInterface itself take?
--
With best wishes,
Igor Tandetnik
With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925