4

I am reading a COM sample at http://msdn.microsoft.com/en-us/library/windows/desktop/dd389098(v=vs.85).aspx

I really cannot comprehend (void **) in

hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);

So I have tried some values returned by different types of pointers by the class

class Point{
private:
    int x, y;
public:
    Point(int inputX, int inputY){x = inputX, y = inputY;}
    int getX(){return x;}
    int getY(){return y;}
    friend ostream& operator << (ostream &out, Point &cPoint);
    Point operator-(){
        return Point(-x, -y);
    }
};

ostream& operator << (ostream &out, Point &cPoint){
    return out<< "(" << cPoint.x << ", " << cPoint.y << ")";
}

and printing out

Point *p = new Point(1,2);
cout << p << endl << &p << endl << endl
<< *&p << endl<< **&p << endl<<endl 
<< (void *) &p << endl << (void **) &p ;

(void*) really has no difference with (void **). What does (void **)&pControl want to return?

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
Hoy Cheung
  • 1,552
  • 3
  • 19
  • 36
  • 1
    `void*` is a pointer to some unknown data type. `void**` has a more specific type, it is a pointer to a `void*`, they are not the same. Unless `pControl` is a `void*` this isn't safe behavior. and if it was a `void*` the cast wouldn't be necessary – Ryan Haining Apr 15 '14 at 18:53
  • 1
    Pointers are *specific* to their type, the lone exception being `void*`, which is simply "untyped". But a pointer-to-(any)-pointer is *always* specifically typed. In this case the underlying decl `void**` is a pointer to pointer-to-void. The cast is needed because `&pControl` evals as the address of a pointer to a specific type (in your case the address of a pointer to whatever type `pControl` is). And `void*`can hold an address from any *data* type; whereas *code* pointers (pointers to functions, member-or-otherwise) are another matter. – WhozCraig Apr 15 '14 at 18:56
  • Not any type, but any **data** type. You can't (safely) have a `void*` assigned a function pointer. `void*` is how generic's get done in C generally, look at [qsort](http://www.cplusplus.com/reference/cstdlib/qsort/) for example. In C++ we have templates and polymorphism so it's not seen nearly as often – Ryan Haining Apr 15 '14 at 18:57
  • "404 Page Not Found" though...maybe this? http://www.learncpp.com/cpp-tutorial/613-void-pointers/ – Hoy Cheung Apr 15 '14 at 18:58
  • @HoyCheung no I just messed up the url syntax, edited. – Ryan Haining Apr 15 '14 at 18:59
  • So in my case of the Point class, &p is already a (Point**). Why the cast (void*) and (void**) return the same number? What happens if casting fails? – Hoy Cheung Apr 15 '14 at 19:05
  • `void*` is a "generic" pointer type. There is no generic pointer-to-pointer type. – Keith Thompson Apr 15 '14 at 19:38
  • If you really want to do something with COM, it is best to get every copy of Don Box Books you can fetch. – Mare Infinitus Apr 15 '14 at 19:48
  • When you `cout` a pointer it is converted to `void *` first, so your various casts will not change the output - it doesn't mean the casts do nothing – M.M Apr 16 '14 at 00:37

1 Answers1

6
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);

What does (void **)&pControl want to return?

QueryInterface() is one of the three methods of IUnknown, which is the base root interface of all COM interfaces.

The MSDN documentation for IUnknown::QueryInterface() clearly states that:

HRESULT QueryInterface(
  [in]   REFIID riid,
  [out]  void **ppvObject
);

ppvObject [out] The address of a pointer variable that receives the interface pointer requested in the riid parameter. Upon successful return, *ppvObject contains the requested interface pointer to the object. If the object does not support the interface, *ppvObject is set to NULL.

So, in your particular case, upon successful return, pControl will contain the requested pointer to the IMediaControl interface, as specified in your function call via the first argument IID_IMediaControl.


Now, let's try to better understand why the double pointer indirection: void**.

void* means "pointer to anything".

So, one might think: "Why isn't the second parameter of QueryInterface() just a void*?"

The problem is that this parameter is an output parameter. This means that QueryInterface() will write something into that parameter, for the caller to use it.

And, in C (and COM has several C-isms), when you have an output parameter, you must use a pointer (*).
(Note In C++ you can also use a reference &.)

So, in this case we have the first level of indirection of void* that means "pointer to anything".
And the second level of indirection (the other *), that means: "This is an output parameter".

You can think of it also in this way:

typedef void* PointerToAnything;

HRESULT QueryInterface(..., /* [out] */ PointerToAnything* pSomeInterface);

// pSomeInterface is an output parameter.
//
// [out] --> use * (pointer), 
// so it's 'PointerToAnything*' (not just 'PointerToAnything'),
// so, with proper substitution, it's 'void**' (not just 'void*').
Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • So why do you want to use &pControl? I thought the way we use objects is pControl->function(). Using &pControl, we need to use (*pControl)->function() which adds up more hassle. – Hoy Cheung Apr 15 '14 at 19:14
  • 1
    In your code, `pControl` should be some pointer to some COM interface (e.g. `IMediaControl*`). But when you pass it to `QueryInterface()`, you must specify _its address_, i.e. the _address of_ `pControl`, so you use `&pControl`. That's because the 2nd parameter of `QueryInterface()` is an **output** parameter. – Mr.C64 Apr 15 '14 at 19:15
  • No, `QueryInterface()` assigns the _address_ of some COM interface to the pointer. But to do that, `QueryInterface()` needs the address of the pointer (i.e. the address of `pControl`, i.e. `&pControl`). – Mr.C64 Apr 15 '14 at 19:19
  • I know what you mean now. Thanks – Hoy Cheung Apr 15 '14 at 19:20