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