Q267939: HOWTO: Use FindText to Enable Searching in a Text Box
Article: Q267939
Product(s): Microsoft Visual Basic for Windows
Version(s): WINDOWS:5.0,6.0
Operating System(s):
Keyword(s): kbAPI kbCmnDlgFind kbSDKWin32 kbVBp500 kbVBp600 kbGrpDSVB kbDSupport
Last Modified: 11-JAN-2001
-------------------------------------------------------------------------------
The information in this article applies to:
- Microsoft Visual Basic Professional Edition for Windows, versions 5.0, 6.0
- Microsoft Visual Basic Enterprise Edition for Windows, versions 5.0, 6.0
-------------------------------------------------------------------------------
SUMMARY
=======
This article shows you how to use the FindText API function to programmatically
enable searching in a Text box for the first match.
MORE INFORMATION
================
The FindText function creates a system-defined, modeless Find dialog box that
lets the user specify a string to search for and options to use when searching
for text in a document. It has one parameter and returns the handle to the Find
dialog box. This parameter is:
- lpfr - pointer to a FINDREPLACE structure that contains information used to
initialize the dialog box. For example, if the FR_MATCHCASE value is used for
the FLAGS field of the structure, the Find dialog box pops up with an enabled
Match Case check box. Such constants can be found in the Text Viewer API
function or in COMMDLG.H.
After you get the handle, you can use the GetWindowLong and SetWindowLong
functions to change the window procedure of the Find Next button. These
functions have two parameters in common:
- hWnd - Handle to the Find dialog box.
- nIndex - Use GWL_WNDPROC here.
SetWindowLong has one additional parameter:
- dwNewLong - The address of the new window procedure.
You write code for the WM_LBUTTONDOWN event in the new window procedure. This
event is fired when the user clicks the Find Next button in the Find dialog box.
Use the IsDlgButtonChecked function to determine whether the Match Case option
is checked. It has two parameters:
- hDlg -- Handle to the Find dialog box.
- nIDButton -- Specifies the identifier of the button control. For Match Case
button, the value is 0x411.
To get the pattern string that is to be matched, you can use the GetDlgItemText
function API. It has four parameters:
- hWnd -- Handle to the text box in the Find dialog box. You can use the
GetDlgItem function to get the value for this parameter.
- nIDDlgItem - Control identifier. This is 0x480.
- lpString - Pointer to the buffer that receives the text.
- nMaxCount - Specifies the maximum number of characters to copy to the buffer,
including the NULL character. If the text exceeds this limit, it is
truncated.
A global string variable is used here to store the current content of the Text
box within which you want to find the matching string. After you find the start
position of the first match, you can use the SendMessage API function to
highlight the matching string in the Text box. SendMessage has four parameters:
- hWnd - Handle to the Text box.
- Msg - Message to be sent. Use EM_SETSEL here.
- wParam - Start position of the matching string.
- lParam - End position of the matching string.
As to the other messages from WM_LBUTTONDOWN, you can use the previous window
procedure to proceed them. You can use the CallWindowProc function here.
CallWindowProc has five parameters:
- lpPrevWndFunc - Pointer to the previous window procedure.
- hWnd - Handle to the Find dialog box.
- Msg - Use the same value as what is passed to the new window procedure.
- wParam - Use the same value as what is passed to the new window procedure.
- lParam - Use the same value as what is passed to the new window procedure.
Step-by-Step Example
--------------------
1. Start a new Standard EXE project in Visual Basic. Form1 is created by
default.
2. Add a CommandButton and a TextBox control to Form1.
3. Copy the following code to the Code window of Form1:
Private Sub Command1_Click()
Dim szFindString As String ' initial string to find
Dim hCmdBtn As Long ' handle of 'Find Next' command button
Dim strArr() As Byte ' for API use
Dim i As Integer ' position indicator in the loop
' Fill in the structure.
szFindString = "Find Me"
ReDim strArr(0 To Len(szFindString) - 1)
For i = 1 To Len(szFindString)
strArr(i - 1) = Asc(Mid(szFindString, i, 1))
Next i
frText.flags = FR_MATCHCASE Or FR_NOUPDOWN Or FR_NOWHOLEWORD
frText.lpfnHook = 0&
frText.lpTemplateName = 0&
frText.lStructSize = Len(frText)
frText.hwndOwner = Me.hwnd
frText.hInstance = App.hInstance
frText.lpstrFindWhat = VarPtr(strArr(0))
frText.lpstrReplaceWith = 0&
frText.wFindWhatLen = Len(szFindString)
frText.wReplaceWithLen = 0
frText.lCustData = 0
' Show the dialog box.
gHDlg = FindText(frText)
' Get the handle of the dialog box
hCmdBtn = GetDlgItem(gHDlg, 1)
' Get necessary value for calling default window procedure.
gOldDlgWndHandle = GetWindowLong(hCmdBtn, GWL_WNDPROC)
If SetWindowLong(hCmdBtn, GWL_WNDPROC, AddressOf FindTextHookProc) = 0 Then
gOldDlgWndHandle = 0
End If
End Sub
Private Sub Form_Load()
gHTxtWnd = Text1.hwnd
End Sub
Private Sub Text1_Change()
gTxtSrc = Text1.Text
End Sub
4. From the Project menu, add a new module to the project and insert the
following code:
Public Type FINDREPLACE
lStructSize As Long ' size of this struct 0x20
hwndOwner As Long ' handle to owner's window
hInstance As Long ' instance handle of.EXE that
' contains cust. dlg. template
flags As Long ' one or more of the FR_??
lpstrFindWhat As Long ' ptr. to search string
lpstrReplaceWith As Long ' ptr. to replace string
wFindWhatLen As Integer ' size of find buffer
wReplaceWithLen As Integer ' size of replace buffer
lCustData As Long ' data passed to hook fn.
lpfnHook As Long ' ptr. to hook fn. or NULL
lpTemplateName As Long ' custom template name
End Type
Public Declare Function FindText Lib "comdlg32.dll" Alias "FindTextA" (pFindreplace As FINDREPLACE) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function GetDlgItem Lib "user32" (ByVal hDlg As Long, ByVal nIDDlgItem As Long) As Long
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function SetFocus Lib "user32" (ByVal hwnd As Long) As Long
Public Declare Function IsDlgButtonChecked Lib "user32" (ByVal hDlg As Long, ByVal nIDButton As Long) As Long
Public Declare Function GetDlgItemText Lib "user32" Alias "GetDlgItemTextA" (ByVal hDlg As Long, ByVal nIDDlgItem As Long, ByVal lpString As String, ByVal nMaxCount As Long) As Long
Public Const GWL_WNDPROC = (-4)
Public Const WM_LBUTTONDOWN = &H201
Public Const FR_NOMATCHCASE = &H800
Public Const FR_MATCHCASE = &H4
Public Const FR_NOUPDOWN = &H400
Public Const FR_UPDOWN = &H1
Public Const FR_NOWHOLEWORD = &H1000
Public Const FR_WHOLEWORD = &H2
Public Const EM_SETSEL = &HB1
Public Const MaxPatternLen = 50 ' Maximum Pattern Length
Global gOldDlgWndHandle As Long
Global frText As FINDREPLACE
Global gTxtSrc As String
Global gHDlg As Long
Global gHTxtWnd As Long
Function FindTextHookProc(ByVal hDlg As Long, ByVal uMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
Dim strPtn As String ' pattern string
Dim hTxtBox As Long ' handle of the text box in dialog box
Dim ptnLen As Integer ' actual length read by GetWindowString
Dim sp As Integer ' start point of matching string
Dim ep As Integer ' end point of matchiing string
Dim ret As Long ' return value for SendMessage
strPtn = Space(MaxPatternLen)
Select Case uMsg
Case WM_LBUTTONDOWN
' Get the pattern string
ptnLen = GetDlgItemText(gHDlg, &H480, strPtn, MaxPatternLen)
' Call default window procedure
If gOldDlgWndHandle <> 0 Then
FindTextHookProc = CallWindowProc(gOldDlgWndHandle, _
hDlg, uMsg, wParam, lParam)
End If
' Customize the winodw procedure
If ptnLen <> 0 Then
strPtn = Left(strPtn, ptnLen)
SetFocus gHTxtWnd
' Get the MatchCase option
If IsDlgButtonChecked(gHDlg, &H411) = 0 Then
sp = InStr(LCase(gTxtSrc), LCase(strPtn))
Else
sp = InStr(gTxtSrc, strPtn)
End If
sp = IIf(sp = 0, -1, sp - 1)
If sp = -1 Then
Call MessageNoFound
End If
ep = Len(strPtn)
ret = SendMessage(gHTxtWnd, EM_SETSEL, sp, sp + ep)
End If
Case Else
' Call the default window procedure
If gOldDlgWndHandle <> 0 Then
FindTextHookProc = CallWindowProc(gOldDlgWndHandle, _
hDlg, uMsg, wParam, lParam)
End If
End Select
End Function
Sub MessageNoFound()
MsgBox "No matches found"
End Sub
5. Press the F5 key to run the program. Input any words you like into the Text
box and click the CommandButton to show the Find dialog box. Then change the
pattern string and click Find Next to highlight the first match in the Text
box. If no match is found, a message box appears indicating that the document
was searched but no match was found.
Additional query words:
======================================================================
Keywords : kbAPI kbCmnDlgFind kbSDKWin32 kbVBp500 kbVBp600 kbGrpDSVB kbDSupport
Technology : kbVBSearch kbAudDeveloper kbZNotKeyword6 kbZNotKeyword2 kbVB500Search kbVB600Search kbVBA500 kbVBA600 kbVB500 kbVB600
Version : WINDOWS:5.0,6.0
Issue type : kbhowto
=============================================================================
THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.
Copyright Microsoft Corporation 1986-2002.