locked
QueryInterface and void** out parameter RRS feed

  • Question

  • Hi all,

     

    I want to know why the 2nd parameter of the QueryInterface method in IUnknown interface is declared as a pointer to pointer variable?

     

    HRESULT QueryInterface(REFIID iid, void ** ppvObject);

     

    I guess if it is returned as a pointer object then we could return IUnknown* instead of void* as per inheritance rules.

     

    Thanks for your help.

     

    Thanks,

    Suresh.

    Wednesday, June 27, 2007 11:09 AM

Answers

  • It is because we need to change the output pointer. So a pointer on its own will not work.

     

    A standard QueryInterface is defined like this.

     

    HRESULT CMyClass::QueryInterface(REFIID riid, void** ppObject)

    {

        HRESULT hr = E_NOINTERFACE;

        *ppObject = NULL;

     

        if(riid == IID_IUnknown)

        {

            *ppObject = (IUnknown)this;

            hr = S_OK;

        }

        if(riid == IID_IMyInterface)

        {

            *ppObject = (IMyInterface)this;

            hr = S_OK;

        }

        if(*ppObject != NULL)

        {

            AddRef();

        }

     

        return hr;

    }

     

    As you can see, you either return a NULL or a pointer to an object. With parameters though, you cannot change the actual parameter itself since it is always copied. That is why pointers are used to allow us to access the memory that the parameter is pointing at.

    Since we need to change a pointer, in this case we use a pointer to a pointer to get around this problem.

    Wednesday, June 27, 2007 11:55 AM
  • In my opinion, a void ** parameter instead of reasonable IUnknown ** was used to make the calling expressions little more convenient. In older C language, if you have a pointer like

     

    struct MyInterface * p;

     

    then its address is automatically converted to void **, therefore you can write:

     

    QueryInterface(..., &p);

     

    instead of

     

    QueryInterface(..., (IUnknown **)&p);

     

    or

     

    QueryInterface(..., (void **)&p);

     

    But in modern C++ you have to write "(void **)&p", since this conversion is not available.

     

    I hope this makes sense.

    Wednesday, June 27, 2007 1:22 PM
  • C++ has stronger type checking, this is especially true for classes. One source for errors in C was the assignment of one type to another and C would allow it. This could turn into a real problem in cases of function pointers or structures and ended up pretty hard to debug.

    C++ on the other hand has strong type checking. If you try to convert one type to another and there are no auto widening/narrowing rules, ie short to int or int to short, then it requires you to explicitly cast. If you could convert classes just like that then there would be all sorts of problems.

    Thursday, June 28, 2007 8:02 AM

All replies

  • It is because we need to change the output pointer. So a pointer on its own will not work.

     

    A standard QueryInterface is defined like this.

     

    HRESULT CMyClass::QueryInterface(REFIID riid, void** ppObject)

    {

        HRESULT hr = E_NOINTERFACE;

        *ppObject = NULL;

     

        if(riid == IID_IUnknown)

        {

            *ppObject = (IUnknown)this;

            hr = S_OK;

        }

        if(riid == IID_IMyInterface)

        {

            *ppObject = (IMyInterface)this;

            hr = S_OK;

        }

        if(*ppObject != NULL)

        {

            AddRef();

        }

     

        return hr;

    }

     

    As you can see, you either return a NULL or a pointer to an object. With parameters though, you cannot change the actual parameter itself since it is always copied. That is why pointers are used to allow us to access the memory that the parameter is pointing at.

    Since we need to change a pointer, in this case we use a pointer to a pointer to get around this problem.

    Wednesday, June 27, 2007 11:55 AM
  • crescens2k has  already explained you the reason. Just keep in mind, it has nothing to do with COM. Plain C++ principles are there.

     

    Wednesday, June 27, 2007 12:18 PM
  • In my opinion, a void ** parameter instead of reasonable IUnknown ** was used to make the calling expressions little more convenient. In older C language, if you have a pointer like

     

    struct MyInterface * p;

     

    then its address is automatically converted to void **, therefore you can write:

     

    QueryInterface(..., &p);

     

    instead of

     

    QueryInterface(..., (IUnknown **)&p);

     

    or

     

    QueryInterface(..., (void **)&p);

     

    But in modern C++ you have to write "(void **)&p", since this conversion is not available.

     

    I hope this makes sense.

    Wednesday, June 27, 2007 1:22 PM
  • Thanks for your help guys.

     

    Now i understand why a pointer to a pointer variable is required.

     

    I have one more question regarding C++ pointers...

     

    Derived **derivedPtr;
      Base **basePtr = derivedPtr; (Derived inherites from Base)

     

    Why this statement is not supported in C++?

    If this works, then the QueryInterface method could send back a IUnknown** instead of void**.

     

    Thanks for your help.

     

    Thanks,
      Suresh.

     

    Thursday, June 28, 2007 3:29 AM
  • C++ has stronger type checking, this is especially true for classes. One source for errors in C was the assignment of one type to another and C would allow it. This could turn into a real problem in cases of function pointers or structures and ended up pretty hard to debug.

    C++ on the other hand has strong type checking. If you try to convert one type to another and there are no auto widening/narrowing rules, ie short to int or int to short, then it requires you to explicitly cast. If you could convert classes just like that then there would be all sorts of problems.

    Thursday, June 28, 2007 8:02 AM