Tuesday, January 04, 2005

 

Some notes on multiframe webpage

1.
如何真正知道DocumentComplete

1.1
作者:111222
出处:http://www.csdn.net

CHtmlView \ WebBrowser2 如何真正知道DocumentComplete (下文针对CHtmlView,由于WebBrowser2与其类似不再累赘。)

虽然CHtmlView提供了下面虚函数来得到文档解析完毕的事件。

virtual void OnDocumentComplete(
LPCTSTR lpszURL
);

但这个事件在网页存在 frame 的时候不再那么有效了,因为一旦某个frame激发了OnDownloadBegin,那么就会激发OnDocumentComplete,结果你会发现你的一个网页激发了多个OnDocumentComplete,而你也不知道哪个OnDocumentComplete真正能告诉你网页处理完毕了。

解决办法有三个:

方法一: 通过CHtmlView::OnProgressChange获取结束事件。 不理会OnDocumentComplete,

在OnProgressChange中有两个参数:
virtual void OnProgressChange(
long nProgress,
long nProgressMax
);

当第一个参数nProgress为-1时表示网页处理完毕。

另外两种方法可以在微软的知识库获得:
http://support.microsoft.com/support/kb/articles/q180/3/66.asp
HOWTO: Determine When a Page Is Done Loading in WebBrowser Control
Q180366

对于其中的方法一有一点补充,在上文support的代码中其在通过调用GetControlUnknown()获得WebBrowser控件的IUnknown指针后调用Release将接口释放了。
这是千万不行的!这将导致你的WebBrowser控件无法清除,详细可以参见MSDN对CWnd::GetControlUnknown的介绍。

1.2

第四个方法,提供超时处理

Creating an Internet Explorer Helper Class
Mustafa Demirhan July 14, 2003
http://www.codeguru.com/Cpp/I-N/internet/browsercontrol/article.php/c6175/

Wait Until the Web Page Is Loaded
After starting to load a Web page using the function above, to wait until the Web page is completely loaded, we can use the READYSTATE property of the IWebBrowser2 object.

bool CMyInternetExplorer::WaitTillLoaded (int nTimeout)
{
READYSTATE result;
DWORD nFirstTick = GetTickCount ();

do
{
m_pWebBrowser->get_ReadyState (&result);

if (result != READYSTATE_COMPLETE)
Sleep (250);

if (nTimeout > 0)
{
if ((GetTickCount () - nFirstTick) > nTimeout)
break;
}
} while (result != READYSTATE_COMPLETE);

if (result == READYSTATE_COMPLETE)
return true;
else
return false;
}

This function waits until the Web page is completely loaded or a timeout occurs. To wait indefinitely, set the nTimeout parameter to 0.

2.
Webbrowser控件访问的页面包含多个Frame如何遍历

2.1 VB的方法
来源:http://blog.joycode.com/technofantasy/posts/13483.aspx

在CSDN上面找过,一般的说法是通过webbrowser1.document.frames.item(0)来访问,还有就是通过分析HTML源码来访问,另外可以通过EnumObjects来访问

Sub EnumFrames(ByVal wb As WebBrowser)
Dim pContainer As olelib.IOleContainer
Dim pEnumerator As olelib.IEnumUnknown
Dim pUnk As olelib.IUnknown
Dim pBrowser As WebBrowser_V1

Set pContainer = wb.Object.Document

' Get an enumerator for the frames
If pContainer.EnumObjects(OLECONTF_EMBEDDINGS, pEnumerator) = 0 Then

Set pContainer = Nothing

' Enumerate and refresh all the frames
Do While pEnumerator.Next(1, pUnk) = 0

On Error Resume Next

' Clear errors
Err.Clear

' Get the IWebBrowser2 interface
Set pBrowser = pUnk

If Err.Number = 0 Then
Debug.Print "Frame: " & pBrowser.LocationName
If (Trim(pBrowser.LocationName) = "left_tree" Then
Text1.Text = pBrowser.Document.body.innerHTML
End If

End If

Loop

Set pEnumerator = Nothing

End If

End Sub

Private Sub Command1_Click()
WebBrowser1.Navigate "http://oakhome.xicp.net/rs_pt/main.htm"
End Sub

Private Sub Command2_Click()
Call EnumFrames(WebBrowser1)
End Sub

另外需要引用ole_lib,这个引用在 http://www.mvps.org/emorcillo/vb6/tlb/tl_ole.zip

另外,http://www.csdn.net/develop/read_article.asp?id=21702 里面的示例代码

void CDlgDocOutline::OnButtonEditFrameSource()
{
HTREEITEM hItem=m_wndTree.GetSelectedItem();
if(hItem==NULL)return;
TVITEM tvi;
ZeroMemory(&tvi,sizeof(tvi));
tvi.mask=TVIF_HANDLE|TVIF_PARAM;
tvi.hItem=hItem;
m_wndTree.GetItem(&tvi);
LPDISPATCH spNode=(LPDISPATCH)tvi.lParam;
CComQIPtr pNode(spNode);
CComQIPtr pElement(pNode);
CComQIPtr pWebBrowser(pElement);
if(pWebBrowser){
CComPtr spDocument;
pWebBrowser->get_Document(&spDocument);
CComQIPtr pDocument(spDocument);
if(pDocument){
CDlgDocOutline DlgDocOutline;
DlgDocOutline.m_pHtmlDoc=pDocument;
DlgDocOutline.DoModal();
BuildDocOutline();
}
}

}

2.2 VC的方法
作者:豆腐
出处:http://blog.joycode.com/roboo/posts/24878.aspx

void getGMailFrame()

{

SHDocVw::IWebBrowser2Ptr spBrowser(spDisp); //生成一个IE窗口的智能指针

if (spBrowser){

spDispatch=NULL;

if (SUCCEEDED(spBrowser->get_Document( &spDispatch)))

pDoc2 = spDispatch;

if(pDoc2!=NULL)

{

pIFrameElementCol=NULL;

CComPtr pIFrameCol;

if (SUCCEEDED(pDoc2->get_frames(&pIFrameCol)))

{

//成功得到页面中所有的Frames

long p=0;

pIFrameCol->get_length(&p);

if(p!=0)

{ //如果有一个以上的Frame,则进一步分析

for(long i=0;i<=(p-1);i++)

{

VARIANT frameRequested;

VARIANT frameOut;

frameRequested.vt = VT_I4 ;

frameRequested.bstrVal = (BSTR)i;

pIFrameCol->item(&frameRequested, &frameOut);

IHTMLWindow2* pIFrameWindow;

frameOut.pdispVal->QueryInterface(IID_IHTMLWindow2, (void**)&pIFrameWindow);

IHTMLDocument2* pIFrameDoc;

pIFrameWindow->get_document(&pIFrameDoc);

ParseDoc(pIFrameDoc);

}

}

}

}

}

}

}

}

2.3 OLE的方法

By 张硕(CathyEagle)
From http://blog.csdn.net/cathyeagle/archive/2004/09/30/121540.aspx

要得到源代码,必须先得到frame。访问frame一般说来有两种方法:

1、通过WebBrowser的文档接口得到frame的集合,再逐一访问。

HRESULT IHTMLDocument2::get_frames(IHTMLFramesCollection2 **p);

由IHTMLFramesCollection2接口的item方法,可以以frame的索引号(从0开始)或frame的名称来访问相应的frame,pvarResult则返回一个IDispatch接口(或一个IDispatch接口的数组,多层嵌套的情况).

HRESULT item(
VARIANT *pvarIndex,
VARIANT *pvarResult
);

例子如下,假设pWin是一个指向主窗口的有效的IHTMLWindow接口指针。

......
VARIANT frameRequested;
VARIANT frameOut;
IHTMLFramesCollection2* pFramesCol;
IHTMLWindow2* pRightFrameWindow;
IHTMLDocument2* pRightDoc;

frameRequested.vt = VT_BSTR;//若为VT_I4则以索引号来访问
frameRequested.bstrVal = L"rightframe";//以名称来访问
//frameRequested.vt = VT_I4;
//frameRequested.bstrVal = (BSTR)0;

hr = pWin->get_frames(&pFramesCol);
hr = pFramesCol->item(&frameRequested, &frameOut);

hr = frameOut.pdispVal->QueryInterface(IID_IHTMLWindow2, (void**)&pRightFrameWindow);
hr = pRightFrameWindow->get_document(&pRightDoc);
......


2、通过IOleContainer枚举嵌入对象的方式来访问WebBrowser对象。

void CMyHtmlView::RefreshFrames()
{
// 取得文档的IDispatch指针
LPDISPATCH lpDisp = NULL;
lpDisp = GetHtmlDocument();

if (lpDisp)
{
IOleContainer* pContainer;
HRESULT hr = lpDisp->QueryInterface(IID_IOleContainer, (void**)&pContainer);
lpDisp->Release();
if (FAILED(hr))
return hr;

IEnumUnknown* pEnumerator;
// 获得枚举器
hr = pContainer->EnumObjects(OLECONTF_EMBEDDINGS, &pEnumerator);
pContainer->Release();
if (FAILED(hr))
return hr;

IUnknown* pUnk;
ULONG uFetched;
// 枚举并刷新所有frame
for (UINT i = 0; S_OK == pEnumerator->Next(1, &pUnk, &uFetched); i++)
{
IWebBrowser2* pBrowser;

hr = pUnk->QueryInterface(IID_IWebBrowser2, (void**)&pBrowser);
pUnk->Release();
if (SUCCEEDED(hr))
{
pBrowser->Refresh();
pBrowser->Release();
}
}
pEnumerator->Release();
}


3、访问的多层嵌套frame
注意每个frame又可以包含自己的frame,而上面所说的方法则是针对一个WebBrowser的窗口实现的,并不会涉及到深层的frame。要实现多层嵌套frame的访问,只需要加入一点递归的操作就行了。如对1中的pRightFrameWindow和2中的pBrowser,将函数稍加修改,在得到两个指针后作递归调用即可。


4、访问源代码
下面的方法来自CHtmlView,是比较正规的方法(能够保持网页的原始格式)。


BOOL CHtmlView::GetSource(CString& refString)
{
BOOL bRetVal = FALSE;
CComPtr spDisp = GetHtmlDocument();

if (spDisp != NULL)
{
HGLOBAL hMemory;
hMemory = GlobalAlloc(GMEM_MOVEABLE, 0);
if (hMemory != NULL)
{
CComQIPtr spPersistStream = spDisp;
if (spPersistStream != NULL)
{
CComPtr spStream;
if (SUCCEEDED(CreateStreamOnHGlobal(hMemory, TRUE, &spStream)))
{
spPersistStream->Save(spStream, FALSE);

LPCTSTR pstr = (LPCTSTR) GlobalLock(hMemory);
if (pstr != NULL)
{
// Stream is always ANSI, but CString
// assignment operator will convert implicitly.

bRetVal = TRUE;
TRY
{
refString = pstr;
}
CATCH_ALL(e)
{
bRetVal = FALSE;
DELETE_EXCEPTION(e);
}
END_CATCH_ALL

if(bRetVal == FALSE)
GlobalFree(hMemory);
else
GlobalUnlock(hMemory);
}
}
}
}
}

return bRetVal;
}

顺便说两个设计网址

叮当模板网
http://www.dingdang.cn/

中国UI设计网 - ChinaUI.com
http://www.chinaui.com/default.asp



<< Home

This page is powered by Blogger. Isn't yours?