在CHtmlView中判断页面加载完成(转)

论坛上有人问如何在CHtmlView中判断页面加载完成。这里给出一点想法。 {articleparagraphlist,default}

 

在CHtmlView中判断页面加载完成(1)

 

 

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值