As more people migrate away from Word 2003 and 2007 to a newer version, more bugs are starting to come out of the woodwork. Again, this one was brought up in a forum.

When a developer writes code to use Word’s built-in Find functionality, it’s usually to do a “Replace all”, or to run a loop that stops after every “hit” until the entire search Range has been processed. It’s therefore common practice to pass wdFindStop to the Wrap property (or argument of the Execute method).

It’s only rarely that we see code using wdFindAsk, so perhaps it’s not surprising that the bug hadn’t been reported previously by earlier adapters of Word 2010…

In the problem scenario, Find should only run on the current selection, then ask the user whether it should continue to execute across the entire document. This is quite legitimate and imitates the user interface. What’s happening is that Find does not stop after checking the current selection, but continues to run for the entire document (does the same as wdFindContinue).

Interestingly enough, wdFindAsk does work correctly when the end of the document is reached, asking the user if the search should continue from the beginning of the document.

Following is some VBA code that works around the problem, so that the user is queried both at the end of the selection and at the end of the document.

Sub FindWdFindAsk()
    Dim rngSelection As word.Range
    Dim rngFind As word.Range
    Dim txtFind As String
    Dim found As Boolean
    Dim valDocEnd As Long
    txtFind = "the"
    valDocEnd = ActiveDocument.content.End
    If Selection.Type  wdSelectionNormal Then
        MsgBox "No selection available to search"
        Exit Sub
    End If
    'We need to be able to always refer back to the original selection
    Set rngSelection = Selection.Range
    Set rngFind = rngSelection.Duplicate

    'This will let us know where we are in the document
    'We should see these numbers only once
    'if we let Word traverse the entire document
    Debug.Print rngFind.Start, rngFind.End
    'Search the selection in a loop.
    'As wdFindStop is being used, found will be False
    'when Execute reaches the end of the selection.
        found = ExecuteFind(rngFind, wdFindStop, txtFind)
        If found Then
            Debug.Print rngFind.Start, rngFind.End
            rngFind.Start = rngFind.End + 1
            rngFind.End = rngSelection.End
        End If
    Loop While found
    'Now ask the user if the search should continue beyond the selection.
    Dim userInput As VbMsgBoxResult
    userInput = _
        MsgBox("The search has reached the end of the selection." & _
               " Do you want to continue?", _
                vbYesNo, "Workaround for wdFindAsk Word 2010/2013")
    'If yes, we let the search continue, this time using wdFindAsk
    'as it still works when the end of the document is reached.
    If userInput = vbYes Then
            rngFind.End = valDocEnd
            found = ExecuteFind(rngFind, wdFindAsk, txtFind)
            'Once the end of the document is reached, 
            'the user will be asked if the search should continue 
            'from the beginning of the document. As long as 
            'the found Range is not within the original starting point
            '(selection), the search continues. Else the loop is exited.
            If found Then
                If Not rngFind.InRange(rngSelection) Then
                    Debug.Print rngFind.Start, rngFind.End
                    rngFind.Start = rngFind.End + 1
                    rngFind.End = valDocEnd
                    Exit Do
                End If
            End If
        Loop While found
End If
End Sub

Private Function ExecuteFind(rngFind As word.Range, _
                             wrap As WdFindWrap, _
                             findText As String) As Boolean
    Dim found As Boolean
    With rngFind.Find
        .Text = txtFind
        .wrap = wrap
        .MatchWholeWord = True
        found = .Execute
    End With
    ExecuteFind = found
End Function

Leave a Reply