现在的位置: 首页 > 综合 > 正文

在CHtmlView中判断页面加载完成

2018年07月25日 ⁄ 综合 ⁄ 共 5335字 ⁄ 字号 评论关闭

论坛上有人问如何在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就能如何实现。

转载:http://blog.csdn.net/wangjia184/article/details/3684862

抱歉!评论已关闭.