tags:

views:

154

answers:

2

I don't know if anyone familiar with BHO (Browser Helper Object), but an expert in c++ can help me too.

In my BHO I want to run the OnDocumentComplete() function only on the main frame - the first container and not all the Iframes inside the current page. (an alternative is to put some code only when this is the main frame).

I can't find how to track when is it the main frame that being populated.

After searching in google I found out that each frame has "IDispatch* pDisp", and I have to compare it with a pointer to the first one.

This is the main function:

STDMETHODIMP Browsarity::SetSite(IUnknown* pUnkSite)
{
    if (pUnkSite != NULL)
    {
        // Cache the pointer to IWebBrowser2.
        HRESULT hr = pUnkSite->QueryInterface(IID_IWebBrowser2, (void **)&m_spWebBrowser);
        if (SUCCEEDED(hr))
        {
            // Register to sink events from DWebBrowserEvents2.
            hr = DispEventAdvise(m_spWebBrowser);
            if (SUCCEEDED(hr))
            {
                m_fAdvised = TRUE;
            }
        }
    }
    else
    {
        // Unregister event sink.
        if (m_fAdvised)
        {
            DispEventUnadvise(m_spWebBrowser);
            m_fAdvised = FALSE;
        }

        // Release cached pointers and other resources here.
        m_spWebBrowser.Release();
    }

    // Call base class implementation.
    return IObjectWithSiteImpl<Browsarity>::SetSite(pUnkSite);
}

This is where I want to be aware whether its the main window(frame) or not:

void STDMETHODCALLTYPE Browsarity::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL)
{
  // as you can see, this function get the IDispatch *pDisp which is unique to every frame.
  //some code
}

I asked this question on Microsoft forum and I got an answer without explaining how to actually implement that: http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/7c433bfa-30d7-42db-980a-70e62640184c

+2  A: 

What jeffdav suggested is, to test wether the pDisp supports IWebBrowser2 via QueryInterface(), and if so, to check wether it is the same object as the one you stored in SetSite().
The QueryInterface() rules only require that a QI for IUnknown always results in the same pointer value, so you have to additionally QI to IUnknown and compare the resulting pointers.

This would lead to something like this in OnDocumentComplete():

IWebBrowser2* pBrowser = 0;
IUnknown *pUnk1=0, *pUnk2=0;
if(   SUCCEEDED(pDisp      ->QueryInterface(IID_IWebBrowser2, (void**)&pBrowser))
   && SUCCEEDED(pDisp      ->QueryInterface(IID_IUnknown,     (void**)&pUnk1))
   && SUCCEEDED(m_spBrowser->QueryInterface(IID_IUnknown,     (void**)&pUnk2))
   && (pUnk1 == pUnk2)) 
{
    // ... top-level
}

... or if you are using ATL (as m_spWebBrowser suggests):

CComQIPtr<IWebBrowser2> spBrowser(pDisp);
if(spBrowser && spBrowser.IsEqualObject(m_spWebBrowser)) {
    // ...
}
Georg Fritzsche
Thank you so much!!!!
shaimagz
Thank you! Helped me with similar problem (i was comparing the wrong interface pointers).IE object model is a mess...
Hernán
@Hernan: Always good to hear that it helped :)
Georg Fritzsche
+1  A: 

Notice that I did not test this, I'm only rewriting what the guy on msdn said.

In ::SetSite you get an IUnknown pointer. Call IUnknown::QueryInterface on it (just like you're already doing), but instead use IID_IDISPATCH. Save this pointer somewhere, this pointer is the top level frame.

In ::OnDocumentComplete you're getting a IDispatch pointer, compare this one to the previous saved ptr and voíla, if there is a match you're in the top level.

monoceres