FAQ:两种方法访问多层嵌套的frame

——谨以怀念写邮件回答网友关于Internet Explorer编程问题的青春岁月

Posted by eagleboost on September 30, 2004

本文转载自我2004年在csdn发布的博客

在您的网站拜读了关于TWebBrowser的使用方法,但是一直有一个问题困扰我,就是如何取得frame嵌套frameHTML的原码,我只是知道单个frame如何取得源码,但是多个frame嵌套就没有办法,请教一下!

2004-09-29 23:41:28

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

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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
......
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对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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,是比较正规的方法(能够保持网页的原始格式)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
BOOL CHtmlView::GetSource(CString& refString)
{  
  BOOL bRetVal = FALSE;  
  CComPtr<IDispatch> spDisp = GetHtmlDocument();

  if (spDisp != NULL)  
  {    
    HGLOBAL hMemory;    
    hMemory = GlobalAlloc(GMEM_MOVEABLE, 0);    
    if (hMemory != NULL)    
    {      
      CComQIPtr<IPersistStreamInit> spPersistStream = spDisp;      
      if (spPersistStream != NULL)      
      {        
        CComPtr<IStream> 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;
}