One of the features I've been planning to add to the upcoming FeedDemon 2.6 is an inline search toolbar similar to the one in Firefox, and I'm pleased to announce that I've finally coded it. But it was harder that I anticipated, primarily due to a weird IE bug (more about that later).
In the current version of FeedDemon, hitting Ctrl+F in the browser displays IE's "Find" dialog, shown below:
While this does the job, it's not nearly as useful as Firefox's inline search, which enables highlighting every instance of a keyword on the current page. So I decided to intercept Ctrl+F and display my own inline search toolbar (click for full screenshot):
After a few hours of coding, I had it working great - or so I thought. Then I did an inline search for the word "the" on CNN's web site, and FeedDemon came crashing down. An inline search on Techmeme had the same result. Boom!
Note: The rest of this post is intended for frustrated developers Googling for help on the problem I ran into.
I traced the crash to a call to the Select method of MSHTML's IHTMLTxtRange interface. Now, you might think I'd be happy to figure out where the bug was, but I wasn't - I'm never happy to discover that a bug resides in code I didn't write, since that means hours of trial-and-error (literally) trying to resolve it.
After numerous false starts, I finally discovered that the crash occurred when IHTMLTxtRange.FindText locates a match in text that isn't visible (inside a hidden DIV, for example). Using the Select method on a visible text range works fine, but using it on a range that's hidden results in the less-than-helpful exception "could not complete the operation due to error 800a025e."
Luckily, once I realized the problem, I was able to work around it with some hackish code. The first step was to catch the exception, and then figure out the next visible element after the hidden text range so I could perform a "FindText" on it (in other words, skip over the hidden match). In Delphi code, it looks something like this:
// ...code prior to this calls IHTMLTxtRange.FindText to locate the keyword...
// SafeSelectRange wraps IHTMLTxtRange.Select and returns False when it raises an exception
if SafeSelectRange(txtr) then
Result := True
// get the parent element of this text range
if (txtr.parentElement = nil) then
// find the element after this one
nNextSrcIdx := txtr.parentElement.sourceIndex + 1;
if nNextSrcIdx > iDoc2.all.length then
elNext := iDoc2.all.item(nNextSrcIdx, null) as IHTMLElement;
if elNext = nil then
// move the text range to this element
// retry the 'FindText' call again (but don't retry forever)
if nNumRetries < MAX_RETRIES then
bRetry := True;
I've stripped a lot of the logic from this code to make it more presentable, but hopefully this is enough to help other developers running into the same hair-pulling bug.