none
what do QISearch and QITABENT do? RRS feed

  • Question

  • just to understand the COM code I am working with I would like to code QueryInterface without using the QITABENT macro and QISearch function. My attempt to do that is not working.  How to manually do what QITAB,  QITABENT and QISearch do?

    here is the QueryInterface function that uses QISearch:

     IFACEMETHODIMP MRecipePreviewHandler::QueryInterface(REFIID riid, void **ppv)
     {
       *ppv = NULL;
    
       static const QITAB qit[] =
       {
         QITABENT(MRecipePreviewHandler, IObjectWithSite),
         QITABENT(MRecipePreviewHandler, IOleWindow),
         QITABENT(MRecipePreviewHandler, IInitializeWithStream),
         QITABENT(MRecipePreviewHandler, IPreviewHandler),
         { 0 },
       };
       
       return QISearch(this, qit, riid, ppv);
     }

    and my replacement that does not work:

    IFACEMETHODIMP MRecipePreviewHandler::QueryInterface(REFIID riid, void **ppv) { *ppv = NULL;

       if ( riid == __uuidof(IUnknown))
       {
         *ppv = this ;
         AddRef( ) ;
         return S_OK ;
       }

    if ( riid == __uuidof(IObjectWithSite)) { *ppv = this ; AddRef( ) ; return S_OK ; } if ( riid == __uuidof(IOleWindow)) { *ppv = this ; AddRef( ) ; return S_OK ; } if ( riid == __uuidof(IInitializeWithStream)) { *ppv = this ; AddRef( ) ; return S_OK ; } if ( riid == __uuidof(IPreviewHandler)) { *ppv = this ; AddRef( ) ; return S_OK ; } *ppv = NULL ; return E_NOINTERFACE ; }



    Thursday, May 24, 2012 4:51 PM

Answers

  • got the answer. Has to do with casting the this pointer to an interface.  Which I don't actually understand.  An explanation here: http://stackoverflow.com/questions/1742848/why-exactly-do-i-need-an-explicit-upcast-when-implementing-queryinterface-in-a

     IFACEMETHODIMP MRecipePreviewHandler::QueryInterface(REFIID riid, void **ppv)
     {
       *ppv = NULL;
       if (( riid == __uuidof(IUnknown)) || ( riid == __uuidof(IObjectWithSite)))
       {
         *ppv = static_cast<IObjectWithSite*>(this) ;
         AddRef( ) ;
         return S_OK ;
       }
    
       else if ( riid == __uuidof(IOleWindow))
       {
         *ppv = static_cast<IOleWindow*>(this) ;
         AddRef( ) ;
         return S_OK ;
       }
    
       else if ( riid == __uuidof(IInitializeWithStream))
       {
         *ppv = static_cast<IInitializeWithStream*>(this) ;
         AddRef( ) ;
         return S_OK ;
       }
    
       else if ( riid == __uuidof(IPreviewHandler))
       {
         *ppv = static_cast<IPreviewHandler*>(this) ;
         AddRef( ) ;
         return S_OK ;
       }
    
       else
       {
         *ppv = NULL ;
         return E_NOINTERFACE ;
       }
     }
    

    • Marked as answer by Steve Richter Thursday, May 24, 2012 5:58 PM
    Thursday, May 24, 2012 5:58 PM

All replies

  • got the answer. Has to do with casting the this pointer to an interface.  Which I don't actually understand.  An explanation here: http://stackoverflow.com/questions/1742848/why-exactly-do-i-need-an-explicit-upcast-when-implementing-queryinterface-in-a

     IFACEMETHODIMP MRecipePreviewHandler::QueryInterface(REFIID riid, void **ppv)
     {
       *ppv = NULL;
       if (( riid == __uuidof(IUnknown)) || ( riid == __uuidof(IObjectWithSite)))
       {
         *ppv = static_cast<IObjectWithSite*>(this) ;
         AddRef( ) ;
         return S_OK ;
       }
    
       else if ( riid == __uuidof(IOleWindow))
       {
         *ppv = static_cast<IOleWindow*>(this) ;
         AddRef( ) ;
         return S_OK ;
       }
    
       else if ( riid == __uuidof(IInitializeWithStream))
       {
         *ppv = static_cast<IInitializeWithStream*>(this) ;
         AddRef( ) ;
         return S_OK ;
       }
    
       else if ( riid == __uuidof(IPreviewHandler))
       {
         *ppv = static_cast<IPreviewHandler*>(this) ;
         AddRef( ) ;
         return S_OK ;
       }
    
       else
       {
         *ppv = NULL ;
         return E_NOINTERFACE ;
       }
     }
    

    • Marked as answer by Steve Richter Thursday, May 24, 2012 5:58 PM
    Thursday, May 24, 2012 5:58 PM
  • Yes, that wouldn't work. You see, when you inherit multiple interfaces, this goes into multiple inheritance territory. When you write a QI, you need to cast the this pointer to the type that it is asking for. So for a QI which is there for an object that exposes an IThisInterface interface, it would be:

    HRESULT MyObject::QueryInterface(REFIID riid, void **ppv)
    {
        *ppv = NULL;
        if(riid == IID_IUnknown || riid == IID_IThisInterface)
        {
            *ppv = static_cast<IThisInterface *>(this);
            AddRef();
            return S_OK;
        }
    
        return E_NOINTERFACE;
    }

    and for a QI for an object that exposes IThisInterface and IThatInterface it would be:

    HRESULT MyObject::QueryInterface(REFIID riid, void **ppv)
    {
        *ppv = NULL;
        if(riid == IID_IUnknown || riid == IID_IThisInterface)
        {
            *ppv = static_cast<IThisInterface *>(this);
            AddRef();
            return S_OK;
        }
    
        if(riid == IID_IThatInterface)
        {
            *ppv = static_cast<IThatInterface *>(this);
            AddRef();
            return S_OK;
        }
    
        return E_NOINTERFACE;
    }

    please note how I dealt with IUnknown. It is like this because *ppv = static_cast<IUnknown *>(this); is ambiguous. You can get an IUnknown from either IThisInterface or IThatInterface so the compiler wouldn't know what to do. But since IThisInterface is derived from IUnknown then just casting it like I did is fine (since the first 3 entries of IThisInterface are IUnknown).

    As to what interface to use when casting IUnknown, if you only inherit from a single interface, you don't even need to do this, you can just cast to IUnknown, but otherwise the left most interface is the one to use. When I write left most, what I mean is, if your clas is defined as:

    class myclass : public IThisInterface, public IThatInterface
    {
    //implementation here
    };

    notice how IThisInterface is the first class inherited? This makes it the left most one in the inheritance list. That is the one to use. This is convention though, using IThatInterface wouldn't be devistating since it wouldn't break anything badly, but IThisInterface has the advantage of being the very first interface. That means it's vtable is the same address as te class instance being passed around so the compiler doesn't need to do any fixing up of the this pointer, and so you don't waste instructions. But as you can see, it isn't critical.

    If you are writing a COM object that can be aggregated then you will need to change the QI a bit. You see, the class AddRef isn't the one you want to call, the one you want is from the interface you just casted to. My usual implementation of QI does this:

    HRESULT MyObject::QueryInterface(REFIID riid, void **ppv)
    {
        HRESULT hr = E_NOINTERFACE;
        *ppv = NULL;
        if(riid == IID_IUnknown || riid == IID_IThisInterface)
        {
            *ppv = static_cast<IThisInterface *>(this);
            hr = S_OK;
        }
    
        if(riid == IID_IThatInterface)
        {
            *ppv = static_cast<IThatInterface *>(this);
            hr = S_OK;
        }
    
        if(SUCCEEDED(hr))
        {
            reinterpret_cast<IUnknown *>(*ppv)->AddRef();
        }
    
        return hr;
    }

    See the reinterpret_cast line? That uses the interface AddReff rather than the class AddRef for reference counting. But well you know if your class is capable of aggregation since you write it that way. But the important part is you must cast the this pointer to your target type.


    This is a signature

    Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Do you want Visual Studio 11 Express to be freely installable on Windows 7 and able to write regular C++ applications? Please vote for this.

    Thursday, May 24, 2012 6:18 PM