1. 程式人生 > >【MFC】轉:在CHtmlView中判斷頁面加載完成

【MFC】轉:在CHtmlView中判斷頁面加載完成

事件處理 什麽 回調 c++ ase BE guid info bool

原帖:http://blog.csdn.net/wangjia184/article/details/3684862

論壇上有人問如何在CHtmlView中判斷頁面加載完成。這裏給出一點想法。

首先想想這個問題如果是在JS裏面是如何實現的。
JS裏面最簡單的方式就是利用onload事件讓一段JS在頁面加載完成後啟動。
使用onload事件的好處是,能夠保證頁面上的image圖片都已經加載完成。

比如:

window.onload = function() 
{
       // do something
}
// 或者
window.attachEvent( "onload", function(){ 
          
// do something } );

這兩種寫法稍有不同。
對於前者,是直接替換了onload的處理句柄,也就是說,如果頁面已經有onload了,那麽執行這句後會導致原本的onload處理會被替換掉(當然,必須保證這句代碼在onload前執行)。
而後者attachEvent,它能夠為onload事件添加多個處理句柄。

插一句, 多個attachEvent上去的事件執行順序是不確定的。 以前有人比較過attachEvent和W3C的addEventListener。
發現attachEvent上去的多個句柄的執行順序完全不可控。這也是為什麽在一些JS框架中,如Ext,引入自己的事件處理流,來實現多瀏覽器的一致性。所以,如果頁面已經attachEvent了, 而你又attachEvent,那麽結果是很難預測了,盡量避免。

前面都是廢話,言歸正傳,如何在CHtmlView裏面來實現?
在CHtmlView中,有下面2個接口函數對應上面的2種JS寫法。

IHTMLWindow2::put_onload(VARIANT v);
// 或者
IHTMLWindow3::attachEvent( BSTR event,
    IDispatch *pDisp,
    VARIANT_BOOL *pfResult
);

看到了吧,在MSHTML中,完全有接口函數來實現JS中同樣的功能。
首先看看 第一種JS寫法對應的 C++ 代碼

void CTestHtmlViewView::OnDocumentComplete(LPCTSTR lpszURL)
{
    IHTMLDocument * pDoc = (IHTMLDocument *)GetHtmlDocument();
    CComQIPtr
<IHTMLDocument2> pDoc2(pDoc); if( pDoc2 ) { CComPtr<IHTMLWindow2> pWnd2; pDoc2->get_parentWindow(&pWnd2); if( pWnd2 ) { VARIANT vEvent = CDOMEventHandler::CreateEventHandlerVariant( &CTestHtmlViewView::OnLoad, (LONG_PTR)this); pWnd2->put_onload( vEvent ); } } CHtmlView::OnDocumentComplete(lpszURL); }

再看看 第2種JS對應的C++代碼。

void CTestHtmlViewView::OnDocumentComplete(LPCTSTR lpszURL)
{
    IHTMLDocument * pDoc = (IHTMLDocument *)GetHtmlDocument();
    CComQIPtr<IHTMLDocument2> pDoc2(pDoc);
    if( pDoc2 )
    {
        CComPtr<IHTMLWindow2> pWnd2;
        pDoc2->get_parentWindow(&pWnd2);
        CComQIPtr<IHTMLWindow3> pWnd3(pWnd2);
        if( pWnd3 )
        {
            VARIANT_BOOL vbSuccess = VARIANT_FALSE;
            pWnd3->attachEvent( _bstr_t(_T("onload"))
                , CDOMEventHandler::CreateEventHandler( &CTestHtmlViewView::OnLoad, (LONG_PTR)this)
                , &vbSuccess
                );
        }   
    }
}

具體使用哪種寫法,依據你自己的實際情況而定。
細心的你一定發現了, CDOMEventHandler是什麽?
CTestHtmlViewView::OnLoad是什麽?

CTestHtmlViewView::OnLoad是onload事件的響應函數,它在這裏作為函數指針傳遞,函數指針原型為:

typedef void (*PFN_DOM_EVENT_CALLBACK)(VARIANT* pVarResult, LONG_PTR lpUserData); 

CDOMEventHandler是一個派生自IDispatch的類。好了,不說那麽多了。那應該具體怎麽做呢?

第一步,將CDOMEventHandler類加入到你的工程

#pragma once
// DOMEventHandler.h
//-------------------------------------------------------------------------
// Created          :2009-1-2 WangJia
//-------------------------------------------------------------------------
typedef void (*PFN_DOM_EVENT_CALLBACK)(VARIANT* pVarResult, LONG_PTR lpUserData);
class CDOMEventHandler : public IDispatch 
{
public:
    CDOMEventHandler(PFN_DOM_EVENT_CALLBACK pfnCallback, LONG_PTR lpUserData);
    ~CDOMEventHandler(void);
    HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject);
    DWORD __stdcall AddRef();
    DWORD __stdcall Release();
    STDMETHOD(GetTypeInfoCount)(unsigned int FAR* pctinfo) { return E_NOTIMPL; }
    STDMETHOD(GetTypeInfo)(unsigned int iTInfo, LCID  lcid, ITypeInfo FAR* FAR*  ppTInfo) { return E_NOTIMPL; }
    STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgDispId) { return S_OK; }
    STDMETHOD(Invoke)(DISPID dispIdMember
        , REFIID riid
        , LCID lcid
        , WORD wFlags
        , DISPPARAMS* pDispParams
        , VARIANT* pVarResult
        , EXCEPINFO * pExcepInfo
        , UINT * puArgErr
        );
    static LPDISPATCH CreateEventHandler(PFN_DOM_EVENT_CALLBACK pfnCallback, LONG_PTR lpUserData);
    static VARIANT CreateEventHandlerVariant(PFN_DOM_EVENT_CALLBACK pfnCallback, LONG_PTR lpUserData);
private:
    PFN_DOM_EVENT_CALLBACK  m_pfCallback;
    long                    m_lRefCount;
    LONG_PTR                m_lpUserData;
};
#include "StdAfx.h"
#include "DOMEventHandler.h"
// DOMEventHandler.cpp
//-------------------------------------------------------------------------
// Created          :2009-1-2 WangJia
//-------------------------------------------------------------------------
CDOMEventHandler::CDOMEventHandler(PFN_DOM_EVENT_CALLBACK pfnCallback, LONG_PTR lpUserData)
: m_lRefCount(0)
, m_pfCallback(pfnCallback)
, m_lpUserData(lpUserData)
{
}
CDOMEventHandler::~CDOMEventHandler(void)
{
}
HRESULT __stdcall CDOMEventHandler::QueryInterface(REFIID riid, void** ppvObject)
{
    *ppvObject = NULL;
    if (IsEqualGUID(riid, IID_IUnknown))
        *ppvObject = reinterpret_cast<void**>(this);
    if (IsEqualGUID(riid, IID_IDispatch))
        *ppvObject = reinterpret_cast<void**>(this);
    if (*ppvObject)
    {
        ((IUnknown*)*ppvObject)->AddRef();
        return S_OK;
    }
    else
    {
        return E_NOINTERFACE;
    }
}
DWORD __stdcall CDOMEventHandler::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}
DWORD __stdcall CDOMEventHandler::Release()
{
    if (InterlockedDecrement(&m_lRefCount) == 0)
    {
        delete this;
        return 0;
    }
    return m_lRefCount;
}
HRESULT CDOMEventHandler::Invoke(DISPID dispIdMember,
                              REFIID riid,
                              LCID lcid,
                              WORD wFlags,
                              DISPPARAMS* pDispParams,
                              VARIANT* pVarResult,
                              EXCEPINFO * pExcepInfo, 
                              UINT * puArgErr)
{
    if (dispIdMember == DISPID_VALUE)
    {
        if( m_pfCallback )
            (*m_pfCallback)(pVarResult, m_lpUserData);
    }
    return S_OK;
}
LPDISPATCH CDOMEventHandler::CreateEventHandler(PFN_DOM_EVENT_CALLBACK pfnCallback, LONG_PTR lpUserData)
{
    CDOMEventHandler * pHandler = new CDOMEventHandler( pfnCallback, lpUserData);
    return reinterpret_cast<LPDISPATCH>(pHandler);
}
VARIANT CDOMEventHandler::CreateEventHandlerVariant(PFN_DOM_EVENT_CALLBACK pfnCallback, LONG_PTR lpUserData)
{
    VARIANT variant;
    variant.vt = VT_DISPATCH;
    variant.pdispVal = CDOMEventHandler::CreateEventHandler( pfnCallback, lpUserData);
    return variant;
}

第2步,添加你自己的回調函數。這裏只是舉例。

class CTestHtmlViewView : public CHtmlView
{
     static void OnLoad(VARIANT* pVarResult, LONG_PTR lpUserData);
};
void CTestHtmlViewView::OnLoad(VARIANT* pVarResult, LONG_PTR lpUserData)
{
    CTestHtmlViewView * pThis = reinterpret_cast<CTestHtmlViewView*>(lpUserData);
    ::MessageBox( NULL, _T("OnLoad"), NULL, MB_OK);
}

第3步,根據你自己的情況,掛接onload, 代碼參考上面。

基本就這樣,如果你的頁面有框架,iframe中的window也是能夠掛接onload的哦, 反正一切都和JS的一致。JS能夠怎麽實現,VC就能如何實現。

【MFC】轉:在CHtmlView中判斷頁面加載完成