Announcement

Collapse
No announcement yet.

Rich edit character positions

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Rich edit character positions

    I am wanting the character positions of the caret in a rich edit control.
    I wrote a function to return the position and it works fine for a regular
    edit control, but yields a page fault for the rich edit control. The code
    below illustrates my problem.

    I hope someone can spot the trouble. Thanks for your time.

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Code:
    #COMPILE EXE
    #DIM ALL
    
    #INCLUDE "WIN32API.INC" 'Some include files
    #INCLUDE "COMMCTRL.INC"
    #INCLUDE "RICHEDIT.INC"
    
    %ID_RichEdit  = 500
    %ID_ShowPos = 501
    GLOBAL hEdit AS LONG
    DECLARE CALLBACK FUNCTION dlgCallback()
    DECLARE FUNCTION getEditCursPos AS LONG
    
    FUNCTION PBMAIN
       LOCAL hDlg AS LONG
       LoadLibrary "RICHED32.DLL"
       CALL InitCommonControls
       DIALOG NEW 0, "DDT RichEdit demo",,, 150, 90, %WS_SYSMENU TO hDlg
       CONTROL ADD TEXTBOX, hDlg, %ID_RichEdit, "", 5, 20, 120, 12
       'CONTROL ADD "RichEdit", hDlg, %ID_RichEdit, "", 5, 20, 120, 12, _
       '            %WS_CHILD + %WS_VISIBLE, %WS_EX_CLIENTEDGE
       CONTROL ADD LABEL, hDlg, %ID_ShowPos, "", 5, 40, 40, 10
       DIALOG SHOW MODAL hDlg CALL dlgCallback
    END FUNCTION
    
    CALLBACK FUNCTION dlgCallback()
       LOCAL s AS STRING
       SELECT CASE CBMSG
       CASE %WM_INITDIALOG
          CONTROL HANDLE CBHNDL, %ID_RichEdit TO hEdit
       CASE %WM_COMMAND
          CONTROL SET TEXT CBHNDL, %ID_ShowPos, STR$(getEditCursPos)
       END SELECT
    END FUNCTION
    
    FUNCTION getEditCursPos() AS LONG
       LOCAL p AS POINTAPI
       LOCAL lParam AS DWORD
       GetCaretPos BYVAL VARPTR(p)
       lParam = p.x
       FUNCTION = SendMessage(hEdit, %EM_CHARFROMPOS, 0, lParam)
    END FUNCTION

  • #2
    If you only want the cursor position, you can use:
    Code:
      LOCAL selStart AS LONG, selEnd AS LONG
      SendMessage hEdit, %EM_GETSEL, VARPTR(selStart), VARPTR(selEnd)
      FUNCTION = selStart
    BTW, the reason why your current code causes a fault is probably
    because you must pass lParam's address, not the value and you also
    must pass the complete x-y pos, like: (not tested)

    Code:
      lParam = MAKLNG(p.x, p.y)
      FUNCTION = SendMessage(hEdit, %EM_CHARFROMPOS, 0, VARPTR(lParam))

    ------------------

    Comment


    • #3
      Actually SendMessage() expects it's parameters BYVAL - check out the declaration and all will be revealed:

      DECLARE FUNCTION SendMessage LIB "USER32.DLL" _
      ALIAS "SendMessageA" (BYVAL hwnd AS LONG, BYVAL wMsg AS LONG, _
      BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
      However, from the looks of the code, LPARAM is actually a register variable, and therefore cannot be passed BYREF anyway - the closest you could get would be to pass it BYCOPY.

      ------------------
      Lance
      PowerBASIC Support
      mailto:[email protected][email protected]</A>
      Lance
      mailto:[email protected]

      Comment


      • #4
        Borje, thanks for responding.

        Your way for finding the caret position is better than mine. I prefer it
        because it is simpler. But it too works fine for the regular edit control,
        but does not work for the rich edit control, although it doesn't give a
        page fault. It just doesn't seem to respond.

        I don't believe your reason for mine not working is correct. Although the
        LPARAM is an address for EM_GETSEL, it is the value for EM_CHARFROMPOS.

        Although your method and mine give different responses, I believe the reason
        that neither works for the rich edit control is the same.

        Thanks again for your time. Please give it another look.


        ------------------

        Comment


        • #5
          Lance,

          I've wondered about this for some time. Isn't it true that if the
          SendMessage is declared with BYVAL parameters, that we don't have to
          call the function with BYVAL explicitly in front of each parameter?

          For example,
          Call SendMessage(hEdit, %EM_CHARFROMPOS, WPARAM, LPARAM)
          rather than
          CALL SendMessage(BYVAL hEdit, BYVAL %EM_CHARFROMPOS, BYVAL WPARAM, BYVAL LPARAM)


          ------------------

          Comment


          • #6
            Correct.

            The most common use of BYVAL (within calling code) occurs when you need to pass another variable class to a function that expects a specific variable class by reference (BYREF). This tells the compiler not to perform type checking on the parameter at compile time - it just passes the value to the function in the manner you instructed without checking the parameter for correctness. This forces it to be your responsibility to ensure that you are passing a valid address/parameter as expected by the actual sub/function.

            This is not always a good thing if you do it accidentally - it can cause subtle bugs in your code - but it can be very handy as long as you are careful.

            For example:
            Code:
            DECLARE SUB MySub(a AS LONG)
            ...
            DIM x AS POINTAPI
            CALL MySub(BYVAL VARPTR(x))
            In this example, we are passing the address of the POINTAPI structure to a subroutine that expects a pointer to a LONG integer. Because we are passing a valid address, the code will execute, but the compiler did not type-check the actual parameter being passed.


            However, if we tried:
            Code:
            DECLARE SUB MySub(a AS LONG)
            ...
            DIM x AS QUAD
            CALL MySub(BYVAL x)
            Again, the code is compilable because the BYVAL clause stopped the compiler from type-checking the parameter during compilation, but this code is a potential disaster because we are passing the value stored in x&& to a sub that is expecting the pointer address of a LONG integer!

            This is a classic programming error that is likely to earn you 1 GPF point for trying, or even a stack corruption problem

            ------------------
            Lance
            PowerBASIC Support
            mailto:[email protected][email protected]</A>
            Lance
            mailto:[email protected]

            Comment


            • #7
              Normally you should pass lParam/wParam as they are - without VARPTR.
              When it comes to EM_GETSEL, it's a bit different though.

              The documentation says EM_GETSEL returns the first and last position
              in the LOWRD/HIWRD of the result. However, if you use VARPTR(lParam)
              and VARPTR(wParam) in the call, it also returns the positions in
              those two 32-bit members. Like the documentation says:

              "Value of wParam. Points to a 32-bit value that receives the starting
              position of the selection. This parameter can be NULL." -> "points to.."

              When it comes to Richedit, one should use EM_EXGETSEL instead. The
              following works for Richedit:
              Code:
                LOCAL selData AS CHARRANGE  '.cpMin/.cpMax gives first/last pos.
                SendMessage hEdit, %EM_EXGETSEL, 0, VARPTR(selData)
                FUNCTION = selData.cpMin
              Note that in order to get any notifycation messages from Richedit,
              one must set the eventmask, like this to get EN_UPDATE:

              Code:
                CONTROL HANDLE CBHNDL, %ID_RichEdit TO hEdit
                CALL SendMessage(hEdit, %EM_SETEVENTMASK, 0, %ENM_UPDATE)

              ------------------

              Comment


              • #8
                BTW, just tested and the following works fine for both Richedit
                and the normal edit control:
                (cleaner - but it will "only" return positions up to 65,535.. )
                Code:
                  LOCAL lRes AS LONG
                  lRes = SendMessage(hEdit, %EM_GETSEL, 0, 0)
                  FUNCTION = LOWRD(lRes)

                ------------------

                Comment


                • #9
                  Thank you, Borje, for all of your help. The way you used EM_GETSEL
                  isn't discussed in win32.hlp, but I did find it at the Microsoft site
                  you referenced above. The article of interest there was found under
                  "INFO: Caret Position & Line Numbers in Multiline Edit Controls."

                  Interestingly, there they use the high word return instead of the low
                  word, but since they are the same when WPARAM and LPARAM are both set to 0,
                  it doesn't matter which is used. For this important case of retrieving the
                  caret position, it would seem that Microsoft could have used the full 32 bit
                  value instead of two equal 16 bit values, and not been limited to 65536.

                  Anyway, thanks again, Borje. And thanks also to Lance for his usual insights.


                  ------------------

                  Comment


                  • #10
                    In a rich edit control, you are better off using EM_EXGETSEL as it can
                    select greater than 64k. he following code works on everything from
                    win95 OEM, a, b, c, win 98, se, NT4, win2k with no problems.

                    LOCAL Cr as CHARRANGE

                    SendMessage hEdit,%EM_EXGETSEL,0,VarPtr(Cr)

                    [email protected]


                    ------------------
                    hutch at movsd dot com
                    The MASM Forum

                    www.masm32.com

                    Comment

                    Working...
                    X