当IHTMLTxtRange::findText Method查找不到字符串的时候
/*程序中的源码*/
HRESULT CContentBehavior::InternalFindTextImpl(CComBSTR bstrText, DWORD dwFlag, BOOL bSelected, DWORD *dwRet, BOOL bReplaceFind)
{
if( dwRet)
*dwRet =0;
CStringW strText(bstrText);
BOOL bBlankExists= strText.Find(L' ') >=0;
FXE::CXMLPointerUtil tools(this);
CComPtr<IMarkupPointer> pStart, pEnd, pEndConst;
tools.GetSelectedPointer(&pStart, &pEnd, NULL);
if ( (pStart == NULL) || (pEnd == NULL))
return E_FAIL;
BOOL bAreEqual = FALSE;
pStart->IsEqualTo(pEnd, &bAreEqual);
CComPtr<IHTMLElement> pElem;
GetContentElement(&pElem);
ASSERT(pElem);
long nStep = 1L;
MARKUP_CONTEXT_TYPE type = CONTEXT_TYPE_Text;
long nCount = bstrText.Length();
DWORD dwFlagTmp = dwFlag;
if( dwFlag & FR_DOWN)
{
pEnd->MoveAdjacentToElement(pElem, ELEM_ADJ_BeforeEnd );
if( dwFlag & FR_LOOPFIND)
pStart->MoveAdjacentToElement(pElem, ELEM_ADJ_AfterBegin);
if((!bAreEqual)&&(!bReplaceFind))
pStart->Right(TRUE, &type, NULL, &nCount, NULL);
}
else
{ //替换的时候,调用查找函数,是永远不会进入该else的。
pStart->MoveAdjacentToElement(pElem, ELEM_ADJ_AfterBegin);
if( dwFlag & FR_LOOPFIND)
pEnd->MoveAdjacentToElement(pElem, ELEM_ADJ_BeforeEnd );
if((!bAreEqual)&&(!bReplaceFind))
pEnd->Left(TRUE, &type, NULL, &nCount, NULL);
nCount = -nCount;
}
dwFlag &= (~FR_DOWN);
dwFlag &= (~FR_LOOPFIND);
CComPtr<IHTMLDocument2> spDoc;
GetHTMLDocument(&spDoc);
if( spDoc == NULL)
return E_FAIL;
CComPtr<IHTMLElement> spBodyElement;
spDoc->get_body(&spBodyElement);
ASSERT(spBodyElement);
CComQIPtr<IHTMLBodyElement> spBody(spBodyElement);
CComPtr<IHTMLTxtRange> spRange;
spBody->createTextRange(&spRange);
ASSERT(spRange);
//选中范围
CComQIPtr<IMarkupServices> pServices(spDoc);
pServices->CreateMarkupPointer(&pEndConst);
pEndConst->MoveToPointer(pEnd);
pServices->MoveRangeToPointers(pStart,pEnd,spRange);
ASSERT(spRange);
{
VARIANT_BOOL bRet = VARIANT_FALSE;
HRESULT hr = spRange->findText( bstrText, nCount, dwFlag, &bRet); //没有查找到并不代表spRange中真的没有bstrText这个字符串
CComBSTR bstrTextR;
spRange->get_text(&bstrTextR);
CStringW strTextR(bstrTextR);
CStringW strSearch(bstrText);
strTextR.Replace(L"\r\n",L"\r");//避免换行导致的误差
int n = -1;
if (!(dwFlag & FR_MATCHCASE))
{
strTextR.MakeLower();
strSearch.MakeLower();
}
if (dwFlagTmp & FR_DOWN)
{
n = strTextR.Find(strSearch);
}
else
{
std::wstring stdstrTextR = strTextR.GetBuffer(0);
n = stdstrTextR.rfind(strSearch.GetBuffer(0));
}
bool bFind = false;
if( bRet)
{
bFind = true;
}
else if (-1 != n && 0 == bRet)//如果n != -1,说明文档中是有这个字符串的,IHTMLTxtRange::findText查找不到,但是CStringW的查找方式可以查找到
//那么我们需要解决的一个问题那就是如果通过CStringW查找到的位置,来定位这个字符串在DOM中的位置
{
while(true)
{
long nRealMoved = -1;
HRESULT hrMove = S_OK;
hrMove = spRange->move(CComBSTR(L"character") ,n, &nRealMoved);//IE给我们提供了这么一个接口,那就是IHTMLTxtRange::move函数,其可以在折叠Range后,移动空Range所在的位置,从Range最初的起始位置开始移动,第一个参数CComBSTR(L"character")表示要移动的字符数,但是你一定要记住,这个字符数并非指文本字符的个数,而是指的光标移动的个数,如果期间有图片,表格等非文本元素的时候,一样会占用移动个数。
//好了,如果能通过CStringW查找到的位置,用来移动Range的开始位置,是不是我们就定位到了字符串在文档中的位置了呢?
//关键也就在解决非文本字符暂用移动次数的问题了
if (FAILED(hrMove) || n != nRealMoved)
{
bFind = false;
break;
}
pServices->MovePointersToRange(spRange, pStart,pStart);
nCount = abs(nCount);
hrMove = spRange->move(CComBSTR(L"character") ,nCount, &nRealMoved);
if (FAILED(hrMove) || nCount != nRealMoved)
{
bFind = false;
break;
}
pServices->MovePointersToRange(spRange, pEnd, pEnd);
pServices->MoveRangeToPointers(pStart, pEnd, spRange);
//为了避免非文本格式(图片,音频,视频,表格)造成的误差
//这里也就是解决非文本元素暂用移动次数的解决方式,首先,如果有非文本元素,那么我们移动的次数就会被消耗掉一些,那么我们在还没有将Range移动到应该移动的位置,也就已经消耗完了移动次数,起初的时候,我将图片一类的非文本元素进行次数消耗记录,但是表格这样的非文本元素却非常难以统计,对此,我用了一个方法,那就是循环定位,所谓循环定位,就是在定位后的位置,继续查找要查找的字符串(刚才已经说了,非文本元素只会消耗掉移动次数,而不会凭空减少,所以我们定位不准确只有一种可能,那就是还没有移动到需要定位的字符串,而不可能已经超过了我们要定位的字符串所在的位置),如果刚好查找到,需要移动的次数为0,那也就说明我们刚好定位到了字符串,也就不需要移动了,我们也就在DOM中精确定位到了我们要查找到的字符串
CComPtr<IHTMLTxtRange> spRangeTmp;
spRange->duplicate(&spRangeTmp);
pServices->MoveRangeToPointers(pStart, pEndConst, spRangeTmp);
CComBSTR bstrTextRInner;
spRangeTmp->get_text(&bstrTextRInner);
CStringW strTextRInner(bstrTextRInner);
n = strTextRInner.Find(strSearch);
if (-1 == n)
{
bFind = false;
break;
}
if (0 == n)
{
bFind = true;
break;
}
}
}
if (bFind)
{
pServices->MovePointersToRange(spRange, pStart,pEnd);
if( bSelected)
{
spRange->select();
tools.MoveCaretToPointer(pStart);
spRange->scrollIntoView();
}
else
{
pServices->MoveRangeToPointers(pEnd,pEnd,spRange);
spRange->select();
tools.MoveCaretToPointer(pEnd);
spRange->scrollIntoView();
}
if( dwRet)
*dwRet = 1;
}
else
{
if( dwRet)
*dwRet =2;
}
}
return S_OK;
}