The reason why COM uses void**
with QueryInterface
are somewhat special. (See below.)
Generally, void**
simply means a pointer to void*
, and it can be used for out parameters, ie. parameters that indicate a place where a function can return a value to. Your comment /* [out] */
indicates that the location pointed to by ppvInterface
will be written to.
"Why can parameters with a pointer type be used as out parameters?", you ask? Remember that you can change two things with a pointer variable:
- You can change the pointer itself, such that it points to another object. (
ptr = ...
)
- You can modify the pointed-to object. (
*ptr = ...
)
Pointers are passed to a function by value, ie. the function gets its own local copy of the original pointer that was passed to it. This means you can change the pointer parameter inside the function (1) without affecting the original pointer, since only the local copy is modified. However, you can change the pointed-to object (2) and this will be visible outside of the function, because the copy has the same value as the original pointer and thus references the same object.
Now, about COM specifically:
A pointer to an interface (specified by riid
) will be returned in the variable referenced by ppvInterface
. QueryInterface
achieves this via mechanism (2) mentioned above.
With void**
, one *
is required to allow mechanism (2); the other *
reflects the fact that QueryInterface
does not return a newly created object (IUnknown
), but an already existing one: In order to avoid duplication of that object, a pointer to that object (IUnknown*
) is returned.
If you're asking why ppvInterface
has type void**
and not IUnknown**
, which would seem more reasonable type-safety-wise (since all interfaces must derive from IUnknown
), then read the following argument taken from the book Essential COM by Don Box, p. 60 (chapter Type Coercion and IUnknown):
One additional subtlety related to QueryInterface
concerns its second parameter, which is of type void **
. It is very ironic that QueryInterface
, the underpinning of the COM type system, has a fairly type-unsafe prototype in C++ [...]
IPug *pPug = 0;
hr = punk->QueryInterface(IID_IPug, (void**)&pPug);
Unfortunately, the following looks equally correct to the C++ compiler:
IPug *pPug = 0;
hr = punk->QueryInterface(IID_ICat, (void**)&pPug);
This more subtle variation also compiles correctly:
IPug *pPug = 0;
hr = punk->QueryInterface(IID_ICat, (void**)pPug);
Given that the rules of inheritance do not apply to pointers, this alternative definition of QueryInterface
does not alleviate the problem:
HRESULT QueryInterface(REFIID riid, IUnknown** ppv);
The same limitation applies to references as to pointers as well. The following alternative definition is arguably more convenient for clients to use:
HRESULT QueryInterface(const IID& riid, void* ppv);
[...] Unfortunately, this solution does not reduce the number of errors [...] and, by eliminating the need for a cast, removes a visual indicator that C++ type safety might be in jeopardy. Given the desired semantics of QueryInterface
, the argument types Microsoft chose are reasonable, if not type safe or elegant. [...]