Apologies if this is a stupid question, but I wasn't clear on why COM pointer arguments are typically cast as (void**)
instead of (IUnknown**)
. And then sometimes IUnknown
pointers are in fact used, like with IObjectWithSite::SetSite
. Can anyone explain this?
Why are COM pointer arguments cast as void instead of IUnknown?
829 Views Asked by AudioBubble AtThere are 3 best solutions below

Important take-away
This has nothing to do with legacy or convenience. The function signatures are the result of COM fundamentals to allow it to work. They are required to be typed the way they are. If you don't feel like reading through this answer, here are the important take-aways: The moral equivalent of a cast in C++ is a call to QueryInterface
in COM. The only time you are allowed to use a C++ cast in COM is when implementing your COM object's QueryInterface
.
Details
Function signatures in COM that expect the address of a void*
(as opposed to an IUnknown*
) as an output parameter can return any interface type. If this were changed to an IUnknown**
in a hypothetical implementation, it would make using COM either impractical, or right-out impossible.
Let's perform 2 thought experiments, starting with the one that would make COM impractical to use:
Let's assume that CoCreateInstance were to return an IUnknown*
instead of the real interface requested through a void*
. In this case, a client would have to immediately call QueryInterface
on the returned IUnknown*
to receive the interface pointer they had asked for1. This isn't practical2.
This immediately leads into the impossible to solve experiment. Let's assume that QueryInterface
returned an IUnknown*
. To get to the real interface pointer, a client would need to call QueryInterface
. But that only returns an IUnknown*
! At that point the consumable interface surface of COM has collapsed into a single interface, IUnknown
.
Whenever COM returns a pointer to an interface, it must return a pointer to the final type. The only programming language type that matches all interface pointers is indeed a void*
.3
This should explain, why output parameters need to be typed void**
rather than IUnknown**
.
For IObjectWithSite::SetSite, on the other hand, the IUnknown*
is an input. The interface still accepts any COM interface, but it needs to be passed as a pointer to the (identity-comparable) IUnknown
interface.
1 COM does not mandate a particular object layout for implementations. Instead, it delegates requests for interface pointers to the respective QueryInterface
implementations.
2 Even when ignoring the immediate need to call Release()
on the IUnknown
to account for the bumped reference count as part of the QI
call.
3 From The Component Object Model: "The only language requirement for COM is that code is generated in a language that can create structures of pointers and, either explicitly or implicitly, call functions through pointers." There is deliberately no requirement, that interface inheritance be implemented using language-level constructs. Even when IFoo
is required to implement IUnknown
, there is no relationship between IFoo*
and IUnknown*
in a programming language like C.

I guess you are talking about out-parameters that "return" a value via a pointer, e.g.:
IUnknown *u;
QueryInterface(IID_IUnknown, (void **)&u);
IDispatch *d;
QueryInterface(IID_IDispatch, (void **)&d);
Note: See here if you are unfamiliar with the concept of using pass-by-pointer to "return" values.
The prototype of QueryInterface
is:
HRESULT QueryInterface(REFIID iid, void **ptr);
The argument &u
would have type IUnknown **
already (so there is no reason to cast it to the type it already is, as you seem to be suggesting). The cast is to change the type to the expected type for the function parameter.
In Standard C++ the code above is actually undefined behaviour (strict aliasing violation -- you can not pretend a pointer points to a different type object than it really does). The correct usage of this function is:
void *temp;
QueryInterface(IID_IFoo, &temp);
IFoo *foo = static_cast<IFoo *>(temp);
However the Microsoft compiler supports the version with the C-style cast. It makes the same changes to the memory location as if the interface pointer had been declared as void *
instead of its real type.
Why does QueryInterface
take void **
and not IUnknown **
? Well, that would be nice to avoid the cast if you are specifically requesting IID_IUnknown
, however any other interface would require the C-style cast anyway. And it might cause confusion since (for other interfaces) the returned pointer is not necessarily a valid IUnknown *
value .
In C++ programming you can (and perhaps should) use template wrapper classes that perform all the right type manipulations . The Windows API calls are compatible with C so they can not include strongly-typed generics .
NB. All calls to QueryInterface
should check the return value, I omitted that here for brevity.
In "get-type" interface methods (like
IObjectWithSite::QueryInterface
,IObjectWithSite::GetSite
,IMoniker::BindToObject
, etc...), because it wouldn't change anything, you'd have to cast anyway, except when you do require anIUnknown*
reference, but you already have it because ... you're using it (theIUknown*
reference is always the same pointer per COM rules).IObjectWithSite::SetSite
is a "set-type" method, so it makes more sense to give you anIUnknown*
reference.It's probably more arguable in some static methods like CoCreateInstance or CoGetObject I think they could have put
IUnknown**
there instead ofvoid**
but then, they would have two different styles. And you wouldn't be able to use the old IID_PPV_ARGS macro that's so practical to use, and recommended as a coding practice to avoid type cast errors.I suggest you get a copy of the authoritative "Essential COM" from Don Box, and read up to page 60 (at least :-).