基于ie内核的网页高亮查找

    技术2022-05-18  15

          采用ie内核做浏览器,很多时候需要增加一个查找网页内容,然后高亮的功能。这个功能可以采用微软的com接口来完成。

          最基本的步骤就是先通过IWebBrowser2获取到IHTMLDocument2,然后通过IHTMLDocument2获取到IMarkupContainer,IMarkupServices。通过IMarkupServices创建2个IMarkupPointer指针,这两个IMarkupPointer需要放到IMarkupContainer中去,调用接口IMarkupContainer的方法MoveToContainer即可。

         这些步骤准备好后,即可调用IMarkupPointer的FindText接口进行查找遍历的功能。对于查找到的元素,可以采取IMarkupServices接口的方法CreateElement来添加背景色STYLE="background-color:#00ff00“,添加完背景色后需要将查找到的元素显示到那个位置,采用如下方式:

     VARIANT var;   var.vt = VT_BOOL;   var.boolVal = VARIANT_FALSE;   IHTMLElement->scrollIntoView( var );

     

         去除背景色可以采用IMarkupServices的方法RemoveElement(s);

     

     代码如下:

    void SearchText(IHTMLDocument2* pDoc2,IMarkupContainer** pMarkupcontainer,IMarkupServices **pMarkSer,         IMarkupPointer** pMarkPtrStart,IMarkupPointer** pMarkPtrEnd,int& dwCount) {  HRESULT hr = S_FALSE;

     if( *pMarkupcontainer == NULL )  {   hr = pDoc2->QueryInterface( IID_IMarkupContainer, (void **) pMarkupcontainer );   if( hr != S_OK )    return;  }  if( *pMarkSer == NULL )  {   hr = pDoc2->QueryInterface( IID_IMarkupServices, (void **) pMarkSer );   if( hr != S_OK )    return;  }

     if( *pMarkPtrStart == NULL )  {   // need two pointers for marking   hr = (*pMarkSer)->CreateMarkupPointer(  pMarkPtrStart );   if( hr != S_OK )    return;  }  if( *pMarkPtrEnd == NULL )  {   // beginning and ending position of text.   hr = (*pMarkSer)->CreateMarkupPointer(  pMarkPtrEnd );   if( hr != S_OK )    return;  }

     // Set gravity of this pointer so that when the replacement text  // is inserted it will float to be after it.  hr = (*pMarkPtrStart)->SetGravity( POINTER_GRAVITY_Right );  if( hr != S_OK )   return;  hr = (*pMarkPtrEnd)->SetGravity( POINTER_GRAVITY_Left );  if( hr != S_OK )   return;

     // Start the search at the beginning of the primary container  hr = (*pMarkPtrStart)->MoveToContainer( *pMarkupcontainer, TRUE );  if( hr != S_OK )   return;  hr = (*pMarkPtrEnd)->MoveToContainer( *pMarkupcontainer, FALSE );  if( hr != S_OK )   return;

     DWORD dwFlags = 0;  if( m_bMatchCase )   dwFlags |= FINDTEXT_MATCHCASE;

     for( ; ;)  {    hr = (*pMarkPtrStart)->FindText( m_bszSearchText, dwFlags, *pMarkPtrEnd, NULL );

      if (hr == S_FALSE) // did not find the text    break;   

      m_nCount ++;   dwCount++;   //pIHtmlElement->get_parentElement(&pParentElement);   CComPtr<IHTMLElement> pIHtmlElement;   bool bFlag = false;

      (*pMarkPtrStart)->CurrentScope(&pIHtmlElement);   if( pIHtmlElement != NULL )   {       for ( ; ;)    {     CComQIPtr<IHTMLElement2, &IID_IHTMLElement2> pIHtmlElement2(pIHtmlElement);     CComPtr<IHTMLElement> pParentElement = NULL;

        if (S_OK != pIHtmlElement->get_parentElement(&pParentElement) || NULL == pParentElement)      break;

        CComPtr<IHTMLCurrentStyle> pHtmlStyle = NULL;

        pIHtmlElement2->get_currentStyle(&pHtmlStyle);

        CComBSTR bszDisplay, bszVisible;

        if( pHtmlStyle != NULL )     {      pHtmlStyle->get_display(&bszDisplay);      pHtmlStyle->get_visibility(&bszVisible);     }

        if (0 == StrCmpI( _T("none") , bszDisplay) || 0 == StrCmpI(_T("hidden") , bszVisible) )     {      bFlag = true;      m_nCount --;      dwCount--;      break;     }

        bFlag = false;     pIHtmlElement = pParentElement;    }   }

      if( m_bHighlight && !bFlag )   {    InsertMarkup( *pMarkSer, *pMarkPtrStart, *pMarkPtrEnd, false, false );   }

      // Continue searching   (*pMarkPtrStart)->MoveToPointer( *pMarkPtrEnd);  }  }

     

     

          有些复杂的页面采取frame的方式,如csdn的论坛页面,这时候需要遍历出所有的frame中的document

    网页的布局如下:

     

     

    可以采用递归遍历frame的方式,然后遍历出document,再采用开头的方式即可查找

     

    递归如下:

     

    void SearchInFrame(IHTMLDocument2 *pDoc) {  CComPtr<IHTMLFramesCollection2> frames;  HRESULT hr = pDoc->get_frames(&frames);

     if(SUCCEEDED(hr) && frames)  {   long p = 0;   frames->get_length(&p);   for(long f = 0;f<p;f++)   {    variant_t frame;    frames->item(&_variant_t(f),&frame);    if((IDispatch*)frame)    {     CComPtr<IHTMLWindow2>  elem;     hr = ((IDispatch*)frame)->QueryInterface(IID_IHTMLWindow2, (void**)&elem);     if(SUCCEEDED(hr) && elem)     {      CComPtr<IHTMLDocument2>  lpHtmlDocument;      hr  = elem->get_document(&lpHtmlDocument);      if (SUCCEEDED(hr) && lpHtmlDocument)      {

          SEARCH_INFO _info = {0};       SearchText(lpHtmlDocument,&_info.m_pContainer,&_info.m_pServices,&_info.m_pPointerStart,&_info.m_pPointerEnd,_info.m_TotalCount);       _info.m_nCurPos = SEARCH_BEGIN;       if (_info.m_TotalCount > 0)       {        m_listSearchInfo.push_back(_info);       }

          SearchInFrame(lpHtmlDocument);      }

         

        }    }   }  } }

     

    在我们查找到相应的字符后需要高亮,并且让网页滚动到相应位置,如果采用IHTMLElement 接口的scrollIntoView 会导致某些页面发生页面布局变化错位的问题,这是的确非常棘手。还好,ie还有一套接口可以高亮查找,IHTMLTxtRange,采用IHTMLTxtRange的scrollIntoView 可以解决这个问题,所以如果要非常完美的实现高亮查找功能,需要两者结合用

     


    最新回复(0)