Re: CComPtr strange behaviour

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance



Alexander Lamaison <awl03@xxxxxxxxxxxx> 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


.



Relevant Pages

  • Re: CComPtr strange behaviour
    ... the caller just casts it to the expected interface type, ... GetUIObjectOf takes an IUnknown* pointer it got from folder_item_object, ... GetUIObjectOf has basically lied to the caller? ...
    (microsoft.public.vc.atl)
  • Re: Style (?) issue
    ... the pointer is sometimes null, ... It depends on the code whether you want to have the test inside "probe_subtree" or inside the caller, but either way the test is part of the algorithm, not a check for argument validity. ... more than 8 digits" So, addsatisfies ... One is to specify that the function will take two values that can be summed to an eight digit number, and it will return that sum. ...
    (comp.arch.embedded)
  • Re: Style (?) issue
    ... identifying it at compile-time in the caller. ... placed on a pointer passed to it. ... with the contractual requirements of the function (the contract ...
    (comp.arch.embedded)
  • Re: Why can I not alloc memory in called function?
    ... but no caller can detect that it has or has not done ... I use T* when I'm expecting to be passed a pointer to a single T and T ... it is "common knowledge" that the function should be ... Error indications would be the common exception, and the C convention ...
    (comp.lang.c)
  • Re: Should function argument be changed in function body?
    ... involved but even with something as simple as this it can make the caller ... handle an extra type that isn't natural to the problem. ... > You should try to avoid passing a non const pointer to a function unless ... It is probably more common for this reason for the status to be the return ...
    (comp.lang.c)