Using Windows API Functions to Better Manipulate Text Boxes

Last reviewed: May 7, 1996
Article ID: Q141073
The information in this article applies to:
  • Professional and Enterprise Editions of Microsoft Visual Basic, 16-bit only, for Windows, version 4.0

SUMMARY

By calling Windows API functions from Microsoft Visual Basic for Windows, you can retrieve text box (or edit control) information that you cannot obtain using only Visual Basic for Windows's built-in features.

NOTE: In Visual Basic versions 2.0, 3.0, and 4.0 for Windows, you can use the new HWND property of a text box instead of calling the GetFocus() function.

This article supplies a sample program that performs the following useful features (making use of the Windows message constants shown in parentheses, obtained by calling Windows API routines):

  • Copies a specific line of text from the text box (EM_GETLINE).
  • Retrieves the number of lines within the text box (EM_GETLINECOUNT).
  • Positions the cursor at a specific character location (EM_GETSEL) in the text box.
  • Retrieves the line number of a specific character location in the text box (EM_LINEFROMCHAR).
  • Retrieves the amount of lines before a specified character position in the text box (EM_LINEINDEX).
  • Retrieves the amount of characters in a specified line in the text box (EM_LINELENGTH).
  • Replaces specified text with another text string (EM_REPLACESEL).

For a separate article that explains how to specify the amount of text allowable within a text control, query on the following word in the Microsoft Knowledge Base:

   EM_LIMITTEXT

MORE INFORMATION

NOTE: As of 3/25/92, the code below corrects the VBKNOWLG.HLP file shipped with the Microsoft Professional Toolkit for Visual Basic version 1.0 for Windows.

NOTE: Using the SelStart, SelLength, and SelText properties may be easier than using EM_GETSEL and EM_REPLACESEL below.

The Windows API file USER.EXE defines the SendMessage function that returns or performs a specific event on your edit control. To create an example that displays specific information about your edit control, do the following:

  1. Create a form (Form1), and add the following controls and properties:

       Control    Control Name    Height   Left    Top     Width
       ------------------------------------------------------------
       Label      aGetLine                 360     120
       Label      aGetLineCount            360     480
       Label      aGetSel                  360     840
       Label      aLineFromChar            360     1200
       Label      aLineIndex               360     1560
       Label      aLineLength              360     1920
       Label      aReplaceSel              360     2280
       Command    Command1        375      360     2640    1815
       Text       Text1           1815     2640    480     3495
       Text       Text2           375      2520    2640    3615
    
    

  2. Set each label's AutoSize property to True.

  3. Set the Text1.MultiLine property to True.

  4. Change the Command1.Caption to "Insert this text --->".

  5. Add the following code to the global Declarations section of Form1:

       Private Declare Function GetFocus% Lib "user" ()
       ' Enter the following Declare as one, single line:
       Private Declare Function SendMessage& Lib "user"(ByVal hWnd%, ByVal
    
wMsg%,

         ByVal wParam%, ByVal lParam As Any)
   ' lParam is actually a double word, or long, but declaring
   ' lParam "As Any" allows flexibility for certain cases of
   ' using SendMessage.

  • After adding the code listed below to your form, run the program. Whenever a key is released, the labels will be updated with the new information about your text box.

       Private Sub Form_Load ()
          Show
          X% = fReplaceSel("")   '* Used to display the correct text.
       End Sub
    
       Private Sub Text1_KeyUp (KeyCode As Integer, Shift As Integer)
          '* Update the text control information whenever the key
          '* is pressed and released.
          CharPos& = fGetSel()
          LineNumber& = fLineFromChar(CharPos&)
          X% = fGetLine(LineNumber&)
          X% = fGetLineCount()
          X% = fLineIndex(LineNumber&)
          X% = fLineLength(CharPos&)
       End Sub
    
       Private Sub Command1_Click ()
          '* This routine will insert a line of text at the current location
          '* of the caret.
    
          D$ = Text2.text
          CharPos& = fGetSel()
          X% = fReplaceSel(D$)
          X% = fSetSel(CharPos&)
    
          '* Text has been inserted at the caret location.  No update the
          '* text controls information.
          Call Text1_KeyUp(0, 0)
          Text1.SetFocus
       End Sub
    
       Private Function fGetLineCount& ()
          '* This function will return the number of lines in the edit control.
          Const EM_GETLINECOUNT = &H400 + 10
    
          Text1.SetFocus
          ' In versions 2.00 and 3.00, you need to use a long integer to avoid
          ' a bad DLL calling convention error message. As an alternative,
          ' you can use the new HWND property instead of GetFocus():
          Pos& = SendMessage(GetFocus(), EM_GETLINECOUNT, 0&, 0&)
          ' Use the following Pos& if you have Visual Basic version 1.0:
          ' Pos& = SendMessage(GetFocus(), EM_GETLINECOUNT, 0%, 0%)
          aGetLineCount.Caption = "GetLineCount = " + Str$(Pos&)
          fGetLineCount = Pos&
       End Function
    
       Private Function fGetLine (LineNumber As Long)
          '* This function copies a line of text specified by LineNumber
          '* from the edit control. The first line starts at zero.
    
          Const MAX_CHAR_PER_LINE = 80
          Const EM_GETLINE = &H400 + 20
    
          byteLo% = MAX_CHAR_PER_LINE And (255)  '[changed 3/25/92]
          byteHi% = Int(MAX_CHAR_PER_LINE / 256) '[changed 3/25/92, two lines:]
          Buffer$ = chr$(byteLo%) + chr$(byteHi%) + Space$(MAX_CHAR_PER_LINE-2)
    
          Text1.SetFocus
          Pos& = SendMessage(GetFocus(), EM_GETLINE, CINT(LineNumber), Buffer$)
          aGetLine.Caption = "GetLine = " + Buffer$
          fGetLine = Pos&
    
       End Function
    
       Private Function fGetSel& ()
          '* This function returns the starting/ending position of the
          '* current selected text.  This is the current location of the
          '* cursor if start is equal to ending.
          '* LOWORD-start position of selected text
          '* HIWORD-first no selected text
    
          Const EM_GETSEL = &H400 + 0
    
          Text1.SetFocus
          location& = SendMessage(GetFocus(), EM_GETSEL, 0, 0&)
          ending% = location& \ &H10000
          starting% = location& And &H7FFF
          aGetSel.Caption = "Caret Location = " + Str$(starting%)
          fGetSel = location& mod 65536
       End Function
    
       Private Function fLineFromChar& (CharPos&)
          '* This function will return the line number of the line that
          '* contains the character whose location(index) specified in the
          '* third argument of SendMessage. If the third argument is -1,
          '* then the number of the line that contains the first character
          '* of the selected text is returned. If start = end from GetSel,
          '* then the current caret location is used. Line numbers start
          '* at zero.
    
          Const EM_LINEFROMCHAR = &H400 + 25
    
          Text1.SetFocus
          Pos& = SendMessage(GetFocus(), EM_LINEFROMCHAR, CINT(CharPos&), 0&)
          aLineFromChar.Caption = "Current Line = " + Str$(Pos&)
          fLineFromChar = Pos&
       End Function
    
       Private Function fLineIndex (LineNumber As Long)
          '* This function will return the number of bytes that
          '* precede the given line. The returned number reflects the CR/LF
          '* after each line. The third argument to SendMessage specifies
          '* the line number, where the first line number is zero. If the
          '* third argument to SendMessage is -1, then the current line
          '* number is used.
    
          Const EM_LINEINDEX = &H400 + 11
    
          Text1.SetFocus
          Pos& = SendMessage(GetFocus(), EM_LINEINDEX, CINT(LineNumber), 0&)
          aLineIndex.Caption = "#Char's before line = " + Str$(Pos&)
          fLineIndex = Pos&
       End Function
    
       Private Function fLineLength& (CharPos As Long)
          '* This function will return the length of a line in the edit
          '* control. CharPos specifies the index of the character that
          '* is part of the line that you would like to find the length. If
          '* this argument is -1, the current selected character is used as
          '* the index.
    
          Const EM_LINELENGTH = &H400 + 17
          Text1.SetFocus
          Pos& = SendMessage(GetFocus(), EM_LINELENGTH, CINT(CharPos), 0&)
          aLineLength.Caption = "LineLength = " + Str$(Pos&)
          fLineLength = Pos&
       End Function
    
       Private Function fSetSel& (Pos&)
          '* This function selects all characters in the current text that
          '* are within the starting and ending positions given by
          '* Location. The low word is the starting position and the high
          '* word is the ending position. If you set start to end, this
          '* can be used to position the cursor within the edit control.
    
          Const EM_SETSEL = &H400 + 1
          location& = Pos& * 2 ^ 16 + Pos&
          Text1.SetFocus
          X% = SendMessage(GetFocus(), EM_SETSEL, 0%, location&)
          fSetSel = Pos&
       End Function
    
       Private Function fReplaceSel (Buffer$)
          '* This function will replace the current selected text with the
          '* new text specified in Buffer$. You must call SendMessage with
          '* the EM_GETSEL constant to select text.
    
          Const EM_REPLACESEL = &H400 + 18
          Text1.SetFocus
          Pos& = SendMessage(GetFocus(), EM_REPLACESEL, 0%, Buffer$)
          aReplaceSel.Caption = "String inserted = " + Buffer$
          fReplaceSel = Pos&
       End Function
    
    

    REFERENCES

    "Programming Windows: The Microsoft Guide to Writing Applications for Windows 3," Charles Petzold, Microsoft Press, 1990

    "Microsoft Windows Software Development Kit: Reference Volume 1," version 3.0.

    WINSDK.HLP file shipped with Microsoft Windows 3.0 Software Development Kit

    The Professional and Enterprise Editions of Visual Basic 4.0 contain all the necessary SDK references in the MSDN/VB Starter Kit.


  • Additional reference words: 1.00 2.00 3.00 4.00 docerr vb4win vb416
    KBCategory: kbprg kbdocerr kbcode
    KBSubcategory: APrgOther


    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.

    Last reviewed: May 7, 1996
    © 1998 Microsoft Corporation. All rights reserved. Terms of Use.