Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Detecting the IE Refresh button using IWebBrowser2 and DWebBrowserEvents2 events.

0.00/5 (No votes)
14 Oct 2004 1  
An article explaining how to capture a click on Internet Exporer's Refresh button, using DWebBrowserEvents2.

Introduction

If you have ever tried to capture an event from the Internet Explorer web browser when a user clicks the Refresh button, you will know it is not easy. Capturing Internet Explorer events is generally not easy using MFC or COM. I have created a class that I use to capture DWebBrowserEvents2 events and the logic needed to detect a page refresh. There are no events specifically sent for a page refresh and the normal events you check for do not work. So you have to use a work around. My class is called CIEComCtrlSink. It is derived from CCmdTarget and is used to capture DWebBrowserEvents2 events. You can also use the logic to solve this problem in JavaScript etc.

Background

I did some searching on the Internet for a solution to this Microsoft bug/issue and only found some talk of a workout in Google newsgroups. So after I got it working, I thought I better throw the solution up on CodeProject.

Using the code

In the sample application, I just used an MFC Wizard to create a new Dialog project. In the file IERefreshSampleDlg.cpp I respond to a button click and create a new web browser object and advise it we want events from it.

// create the browser and event sink

BOOL CIERefreshSampleDlg::CreateMyIEBrowser()
{
    if(m_pMyIESink != NULL)
    {
        AfxMessageBox("Sorry just one browser in this sample :)");
        return false;
    }

    m_pMyIESink = new CIEComCtrlSink();
    m_pMyIESink->m_pParent = this;
    BOOL bOK = m_pMyIESink->MyAdviseSink();

    return bOK;
}

The CIEComCtrlSink class does all the work. It is derived from CCmdTarget so we can get events from DWebBrowserEvents2 object.

// start capture of DWebBrowserEvents2 events

BOOL CIEComCtrlSink::MyAdviseSink()
{
    // create the web browser using IWebBrowser2

    HRESULT hr = CoCreateInstance(CLSID_InternetExplorer,
                               NULL,
                               CLSCTX_LOCAL_SERVER,
                               IID_IWebBrowser2,
                               (void**)&m_pWebBrowser2);

    if(!SUCCEEDED(hr))
    {
        CString strMsg;
        strMsg.Format("Failed to create object error = %d",(int)hr);
        AfxMessageBox(strMsg);
        return false;
    }
    // get an interface to IWebBrowserApp so we can set the toolbars we want

    hr = m_pWebBrowser2->QueryInterface(IID_IWebBrowserApp,
                                              (void**)&m_pIEApp);
    hr = m_pIEApp->put_StatusBar(true);
    hr = m_pIEApp->put_ToolBar(true);
    hr = m_pIEApp->put_MenuBar(true);

    long hIE;
    // get a window handle so we can resize and show the browser window.

    hr = m_pWebBrowser2->get_HWND(&hIE);
    if(!SUCCEEDED(hr))
    {
        CString strMsg;
        strMsg.Format("Failed to create object error = %d",(int)hr);
        AfxMessageBox(strMsg);
        return FALSE;
    }
    // use CWnd to make showing the window etc easy

    m_wndWebBrowser.Attach((HWND)hIE);
    m_wndWebBrowser.EnableScrollBar(SB_BOTH, ESB_DISABLE_BOTH);
    m_wndWebBrowser.ShowWindow(SW_NORMAL);

    // navigate to a URL

    Navigate2("http://www.stevefoxover.com");

    // advise the browser to send events here

    LPUNKNOWN pUnkSink = GetIDispatch(FALSE);
    AfxConnectionAdvise((LPUNKNOWN)m_pWebBrowser2, 
      DIID_DWebBrowserEvents2,pUnkSink,FALSE,&m_dwCookie); 

    return TRUE;
}

Points of interest

The tricky part is actually working out if the refresh button was hit. The logic goes as follows:

  • Each time BeforeNavigate2() is called we add 1 to a page counter m_nPageCounter ++;
  • Each call to DocumentComplete(), we take 1 from the page counter m_nPageCounter --;
  • Each call to DownloadBegin(), we check if m_nPageCounter == 0. If so we know it's a refresh page call.
  • We also add to an object counter here m_nObjCounter ++;
  • Also we set a member variable to true to note it's a refresh call, m_bIsRefresh = true;
  • On DownloadEnd() we decrease the counter from DownloadBegin. m_nObjCounter --;
  • If m_nObjCounter is zero and we are in refresh mode we know that the refreshed page has loaded. if(m_bIsRefresh && m_nObjCounter == 0)

Clear as mud? Just compile the sample application and put break points in:

  • CIEComCtrlSink::BeforeNavigate2()
  • CIEComCtrlSink::DocumentComplete()
  • CIEComCtrlSink::DownloadBegin()
  • CIEComCtrlSink::DownloadEnd()

You will then see what is called and when from Internet Explorer. IE never calls BeforeNavigate2() or DocumentComplete() on a page refresh. (Bad IE...)

History

My first release...

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here