【MFC】转:在CHtmlView中判断页面加载完成

原帖: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就能如何实现。

原文地址:https://www.cnblogs.com/yvqvan/p/8583658.html

时间: 2024-07-31 02:24:19

【MFC】转:在CHtmlView中判断页面加载完成的相关文章

Webbrowser判断页面加载完成

Webbrowser 请求加载页面,页面中包含各种资源,不能够很准确的判断加载是否完成,需要通过特定的方法判断. 1.使用计数器判断页面是否加载完成.精准可控. // 计数器 int counter = 0 ; // 添加事件响应函数 this .webBrowser.Navigated += new System.Windows.Forms.WebBrowserNavigatedEventHandler(webBrowser_Navigated); this .webBrowser.Docum

JS判断页面加载完毕

//JS判断页面加载完毕,再隐藏加载效果层,一个简单的JS加载效果. document.onreadystatechange = function () { if (document.readyState == "complete") { var page = DF.URLHash.get("page"); if (typeof (page) != "undefined" && page != "page1.html&q

js 判断页面加载状态

//----判断当前页面是否加载状态 开始 ---- document.onreadystatechange = subSomething;//当页面加载状态改变的时候执行这个方法. function subSomething() { if (document.readyState != 'complete') //当页面加载状态 { //----显示遮罩 开始---- $(".overlay").css({ 'display': 'block', 'opacity': '0.8' }

Duilib中Webbrowser事件完善,使其支持判断页面加载完毕

在多iframe的页面中,需要结合DISPID_DOCUMENTCOMPLETE和DISPID_BEFORENAVIGATE2两个事件判断页面是否加载完毕,而duilib中没有提供对DISPID_DOCUMENTCOMPLETE的支持. 要支持DISPID_DOCUMENTCOMPLETE,需要修改UIWebBrowser.cpp.UIWebBrowser.h和WebBrowserEventHandler.h三个文件,修改之后的文件见附件.

jquery中的页面加载方法load()

load方法会在元素的onload事件中绑定一个处理函数,如果处理函数绑定给window对象,则会在所有内容(包括窗口,框架,对象和图片等)加载完毕后触发, 如果处理函数绑定在元素上,则会在元素的内容加载完毕后触发, window.load(function(){ //编写代码 }) 等价于 window.onload=function(){ //编写代码 }

01JQuery笔记-------------------------DOM和jquery中页面加载方法和顺序

<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> <script src="jquery-1.8.3.js"></script&g

Java Selenium (十二) 操作弹出窗口 &amp; 智能等待页面加载完成 &amp; 处理 Iframe 中的元素

一.操作弹出窗口   原理 在代码里, 通过 Set<String> allWindowsId = driver.getWindowHandles(); 来获取到所有弹出浏览器的句柄, 然后遍历, 使用swithcto.window(newwindow_handle)方法. 就可以定位到新的窗口. 测试页面的HTML <html> <head> <title>常见web ui元素操作, 及API使用</title> <script type

ASP.NET中页面加载时文本框(texbox控件)内有文字获得焦点时文字消失

代码如下: <asp:TextBox ID="TextBox1" runat="server" Height="26px" MaxLength="10" Width="166px" Text="请输入用户名" OnFocus="javascript:if(this.value=='请输入用户名') {this.value='';this.style.color='#000

MFC WebBrowser判断网页加载完成

mfc 的webbrowser是大家使用非常多的一种查看操作网页的一种控件,在使用中,判断网页什么时候加载完成就显得非常重要了.下面介绍我采用的一种方法,这种方法可以判断网页内部即使有frame网页也是可以判断成功.在对话框里添加一个webbrowser控件,在对话框的初始化函数OnInitDialog()中加入web控件默认加载的网页. //禁用IE脚本错误 m_web.put_Silent(TRUE); //默认打开百度网页 m_web.Navigate(L"https://www.baid