采用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 可以解决这个问题,所以如果要非常完美的实现高亮查找功能,需要两者结合用