Announcement

Collapse
No announcement yet.

Get Richtext From Another Window

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

  • Get Richtext From Another Window

    There is a reason the I want to do this, I need to debug how the other program is formatting the text. I found this post here, and took the sample at post #7 as a base to build on; (thank you ian mcallister):
    http://www.powerbasic.com/support/pb...ad.php?t=13877
    But it causes the target application to crash. So I'm thinking this is because I'm stepping outside the box and doing something not according to specs. I can interrogate the control:
    Code:
    msgTextMode = SendMessage(hRichEdit, %EM_GETTEXTMODE, 0, 0)
    #DEBUG PRINT "msgTextMode="& BIN$(msgTextMode)
    And the control returns a non-zero value (&h1A), so that works, but attempting to stream from the control fails. With a crash of the target richedit control containing application.
    Code:
    #PBFORMS CREATED V1.51
    '------------------------------------------------------------------------------
    ' The first line in this file is a PB/Forms metastatement.
    ' It should ALWAYS be the first line of the file. Other
    ' PB/Forms metastatements are placed at the beginning and
    ' end of "Named Blocks" of code that should be edited
    ' with PBForms only. Do not manually edit or delete these
    ' metastatements or PB/Forms will not be able to reread
    ' the file correctly.  See the PB/Forms documentation for
    ' more information.
    ' Named blocks begin like this:    #PBFORMS BEGIN ...
    ' Named blocks end like this:      #PBFORMS END ...
    ' Other PB/Forms metastatements such as:
    '     #PBFORMS DECLARATIONS
    ' are used by PB/Forms to insert additional code.
    ' Feel free to make changes anywhere else in the file.
    '------------------------------------------------------------------------------
    
    #COMPILE EXE
    #DIM ALL
    
    '------------------------------------------------------------------------------
    '   ** Includes **
    '------------------------------------------------------------------------------
    #PBFORMS BEGIN INCLUDES
    #RESOURCE "readrich.pbr"
    #INCLUDE "WIN32API.INC"
    #INCLUDE "RichEdit.inc"
    #PBFORMS END INCLUDES
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** Constants **
    '------------------------------------------------------------------------------
    #PBFORMS BEGIN CONSTANTS
    %IDB_GETTEXT        = 1002
    %IDD_READRICHEDIT   =  101
    %IDL_hRichEdit      = 1003
    %IDM_EDIT_COPY      = 1008
    %IDM_EDIT_CUT       = 1009
    %IDM_EDIT_SELECTALL = 1007
    %IDM_FILE_EXIT      = 1006
    %IDR_ACCELERATOR1   =  103
    %IDR_IMGFILE1       =  104
    %IDR_MENU1          =  102
    %IDSB_STATUS        = 1005
    %IDT_hRichEdit      = 1001
    %IDT_RESULTS        = 1004
    #PBFORMS END CONSTANTS
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** Declarations **
    '------------------------------------------------------------------------------
    DECLARE CALLBACK FUNCTION ShowREADRICHEDITProc()
    DECLARE FUNCTION ShowREADRICHEDIT(BYVAL hParent AS DWORD) AS LONG
    DECLARE FUNCTION AttachMENU1(BYVAL hDlg AS DWORD) AS DWORD
    DECLARE FUNCTION AttachACCELERATOR1(BYVAL hDlg AS DWORD) AS DWORD
    DECLARE FUNCTION ASSIGNACCEL(tAccel AS ACCELAPI, BYVAL wKey AS WORD, BYVAL _
        wCmd AS WORD, BYVAL byFVirt AS BYTE) AS LONG
    #PBFORMS DECLARATIONS
    '------------------------------------------------------------------------------
    
    GLOBAL gRichEdit    AS STRING
    
    FUNCTION RichEditStreamGetCallback(BYVAL dwCookie AS DWORD, BYVAL pbBuffer AS BYTE PTR, _
                                        BYVAL cb AS LONG, pcb AS LONG) AS DWORD
       gRichEdit = gRichEdit + PEEK$(pbBuffer, cb)
    END FUNCTION
    
    
    '------------------------------------------------------------------------------
    '   ** Main Application Entry Point **
    '------------------------------------------------------------------------------
    FUNCTION PBMAIN()
        ShowREADRICHEDIT %HWND_DESKTOP
    END FUNCTION
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** CallBacks **
    '------------------------------------------------------------------------------
    CALLBACK FUNCTION ShowREADRICHEDITProc()
    
        SELECT CASE AS LONG CBMSG
            CASE %WM_INITDIALOG
                ' Initialization handler
    
            CASE %WM_SIZE
                ' Dialog has been resized
                LOCAL FormX, FormY, StatusY, ButtonX, ButtonY AS LONG
                CONTROL SEND CBHNDL, %IDSB_STATUS, CBMSG, CBWPARAM, CBLPARAM
                CONTROL GET SIZE CB.HNDL, %IDSB_STATUS TO FormX, StatusY
                DIALOG GET CLIENT CB.HNDL TO FormX, FormY
                CONTROL GET SIZE CB.HNDL, %IDL_hRichEdit TO ButtonX, ButtonY
                CONTROL SET LOC CB.HNDL, %IDT_RESULTS, 0, ButtonY
                FormY = FormY - StatusY - ButtonY
                CONTROL SET SIZE CB.HNDL, %IDT_RESULTS, FormX, FormY
    
    
            CASE %WM_NCACTIVATE
                STATIC hWndSaveFocus AS DWORD
                IF ISFALSE CBWPARAM THEN
                    ' Save control focus
                    hWndSaveFocus = GetFocus()
                ELSEIF hWndSaveFocus THEN
                    ' Restore control focus
                    SetFocus(hWndSaveFocus)
                    hWndSaveFocus = 0
                END IF
    
            CASE %WM_COMMAND
                ' Process control notifications
                SELECT CASE AS LONG CBCTL
                    CASE %IDB_GETTEXT
                        LOCAL hRichEdit, msgTextMode    AS DWORD
                        LOCAL tRichedit                 AS STRING
                        LOCAL eStream                   AS EDITSTREAM
                        CONTROL GET TEXT CB.HNDL, %IDT_hRichEdit TO tRichedit
                        hRichEdit = VAL("&h"& tRichedit)
                    #DEBUG PRINT "Richedit="& HEX$(hRichEdit, 8)
                        msgTextMode = SendMessage(hRichEdit, %EM_GETTEXTMODE, 0, 0)
                    #DEBUG PRINT "msgTextMode="& BIN$(msgTextMode)
                        eStream.dwCookie    = 0
                        eStream.dwError     = 0
                        eStream.pfnCallback = CODEPTR(RichEditStreamGetCallback)
                        SendMessage hRichEdit, %EM_STREAMOUT, %SF_RTF, VARPTR(eStream)
                    #DEBUG PRINT "eStream.dwError="& HEX$(eStream.dwError, 8)
    
                    CASE %IDM_EDIT_COPY
                        CONTROL SEND CB.HNDL, %IDT_RESULTS, %WM_COPY, 0, 0
                        FUNCTION = 1
    
                    CASE %IDM_EDIT_CUT
                        CONTROL SEND CB.HNDL, %IDT_RESULTS, %WM_CUT, 0, 0
                        FUNCTION = 1
    
                    CASE %IDM_EDIT_SELECTALL
                        CONTROL SEND CB.HNDL, %IDT_RESULTS, %EM_SETSEL, 0, -1
                        FUNCTION = 1
    
                    CASE %IDM_FILE_EXIT
                        DIALOG END CB.HNDL
    
                END SELECT
        END SELECT
    END FUNCTION
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** Dialogs **
    '------------------------------------------------------------------------------
    FUNCTION ShowREADRICHEDIT(BYVAL hParent AS DWORD) AS LONG
        LOCAL lRslt AS LONG
    
    #PBFORMS BEGIN DIALOG %IDD_READRICHEDIT->%IDR_MENU1->%IDR_ACCELERATOR1
        LOCAL hDlg  AS DWORD
    
        DIALOG NEW PIXELS, hParent, "Read Rich Edit", 260, 325, 300, 319, _
            %WS_POPUP OR %WS_BORDER OR %WS_THICKFRAME OR %WS_CAPTION OR _
            %WS_SYSMENU OR %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX OR _
            %WS_CLIPSIBLINGS OR %WS_CLIPCHILDREN OR %WS_VISIBLE OR _
            %DS_MODALFRAME OR %DS_3DLOOK OR %DS_NOFAILCREATE OR %DS_SETFONT, _
            %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR _
            %WS_EX_RIGHTSCROLLBAR, TO hDlg
        DIALOG  SET ICON     hDlg, "#" + FORMAT$(%IDR_IMGFILE1)
        CONTROL ADD LABEL,   hDlg, %IDL_hRichEdit, "hRichEdit", 0, 0, 50, 16
        CONTROL ADD TEXTBOX, hDlg, %IDT_hRichEdit, "hRichEdit", 52, 0, 100, 16
        CONTROL ADD BUTTON,  hDlg, %IDB_GETTEXT, "Get Text", 154, 0, 48, 16, _
            %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %BS_TEXT OR _
            %BS_PUSHBUTTON OR %BS_FLAT OR %BS_CENTER OR %BS_VCENTER, %WS_EX_LEFT _
            OR %WS_EX_LTRREADING
        CONTROL ADD TEXTBOX, hDlg, %IDT_RESULTS, "Results", 24, 32, 124, 48, _
            %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %ES_LEFT OR %ES_MULTILINE _
            OR %ES_WANTRETURN OR %WS_VSCROLL, %WS_EX_LEFT OR %WS_EX_LTRREADING _
            OR %WS_EX_RIGHTSCROLLBAR
        CONTROL ADD STATUSBAR, hDlg, %IDSB_STATUS, "Status", 18, 247, 201, 11, %WS_CHILD _
            OR %WS_VISIBLE, %WS_EX_TRANSPARENT OR %WS_EX_LEFT OR _
            %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR
    
        AttachMENU1 hDlg
    
        AttachACCELERATOR1 hDlg
    #PBFORMS END DIALOG
    
        DIALOG SHOW MODAL hDlg, CALL ShowREADRICHEDITProc TO lRslt
    
    #PBFORMS BEGIN CLEANUP %IDD_READRICHEDIT
    #PBFORMS END CLEANUP
    
        FUNCTION = lRslt
    END FUNCTION
    '------------------------------------------------------------------------------
    '------------------------------------------------------------------------------
    FUNCTION AttachMENU1(BYVAL hDlg AS DWORD) AS DWORD
    #PBFORMS BEGIN MENU %IDR_MENU1->%IDD_READRICHEDIT
        LOCAL hMenu   AS DWORD
        LOCAL hPopUp1 AS DWORD
    
        MENU NEW BAR TO hMenu
        MENU NEW POPUP TO hPopUp1
        MENU ADD POPUP, hMenu, "File", hPopUp1, %MF_ENABLED
            MENU ADD STRING, hPopUp1, "Exit" + $TAB + "Alt+F4", %IDM_FILE_EXIT, _
                %MF_ENABLED
        MENU NEW POPUP TO hPopUp1
        MENU ADD POPUP, hMenu, "Edit", hPopUp1, %MF_ENABLED
            MENU ADD STRING, hPopUp1, "Select All" + $TAB + "Ctrl+A", _
                %IDM_EDIT_SELECTALL, %MF_ENABLED
            MENU ADD STRING, hPopUp1, "Copy" + $TAB + "Ctrl+X", %IDM_EDIT_COPY, _
                %MF_ENABLED
            MENU ADD STRING, hPopUp1, "Cut" + $TAB + "Ctrl+X", %IDM_EDIT_CUT, _
                %MF_ENABLED
    
        MENU ATTACH hMenu, hDlg
    #PBFORMS END MENU
        FUNCTION = hMenu
    END FUNCTION
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    FUNCTION AttachACCELERATOR1(BYVAL hDlg AS DWORD) AS DWORD
    #PBFORMS BEGIN ACCEL %IDR_ACCELERATOR1->%IDD_READRICHEDIT
        LOCAL hAccel   AS DWORD
        LOCAL tAccel() AS ACCELAPI
        DIM   tAccel(1 TO 4) AS ACCELAPI
    
        ASSIGNACCEL tAccel(1), %VK_F4, %IDM_FILE_EXIT, %FVIRTKEY OR %FALT OR _
            %FNOINVERT
        ASSIGNACCEL tAccel(2), ASC("A"), %IDM_EDIT_SELECTALL, %FVIRTKEY OR _
            %FCONTROL OR %FNOINVERT
        ASSIGNACCEL tAccel(3), ASC("X"), %IDM_EDIT_COPY, %FVIRTKEY OR %FCONTROL _
            OR %FNOINVERT
        ASSIGNACCEL tAccel(4), ASC("X"), %IDM_EDIT_CUT, %FVIRTKEY OR %FCONTROL OR _
            %FNOINVERT
    
        ACCEL ATTACH hDlg, tAccel() TO hAccel
    #PBFORMS END ACCEL
        FUNCTION = hAccel
    END FUNCTION
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    #PBFORMS BEGIN ASSIGNACCEL
    FUNCTION ASSIGNACCEL(tAccel AS ACCELAPI, BYVAL wKey AS WORD, BYVAL wCmd AS _
        WORD, BYVAL byFVirt AS BYTE) AS LONG
        tAccel.fVirt = byFVirt
        tAccel.key   = wKey
        tAccel.cmd   = wCmd
    END FUNCTION
    #PBFORMS END ASSIGNACCEL
    '------------------------------------------------------------------------------
    Furcadia, an interesting online MMORPG in which you can create and program your own content.

  • #2
    Code:
    >>>>                   CONTROL GET TEXT CB.HNDL, %IDT_hRichEdit TO tRichedit 
                     hRichEdit = VAL("&h"& tRichedit)
                    #DEBUG PRINT "Richedit="& HEX$(hRichEdit, 8)
                        msgTextMode = SendMessage(hRichEdit, %EM_GETTEXTMODE, 0, 0)
                    #DEBUG PRINT "msgTextMode="& BIN$(msgTextMode)
                        eStream.dwCookie    = 0
                        eStream.dwError     = 0
                        eStream.pfnCallback = CODEPTR(RichEditStreamGetCallback)
                        SendMessage hRichEdit, %EM_STREAMOUT, %SF_RTF, VARPTR(eStream)
    At the point where you send the %EM_STREAMOUT, hRichedit is not handle to the richedit control... it is ... well, it's hard to tell what it is.

    I think you want CONTROL HANDLE CBHNDL, %IDT_RICHEDIT to hRichedit. (assuming control was added with CONTROL ADD "<classname>" )

    FWIW, I've seen more of these things messed up in the callback than anywhere else, but you are not even getting to that procedure.
    Last edited by Michael Mattias; 26 Apr 2009, 12:26 PM.
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      Even after sorting out the handle, I don't think it's going to be possible to ask the other application's RE control to stream out the text using a callback function in the address space of your own app?

      You might be better off using the clipboard (like in post no.4 of the same thread).
      Rgds, Dave

      Comment


      • #4
        > need to debug how the other program is formatting the text

        >>I don't think it's going to be possible to ask the other application's ...

        You know I saw that "other program" thing in Mr. Glenn's original post, but the code shown shows no interaction with another application.

        >...RE control to stream out the text using a callback function in the address space
        > your own app

        Actually, it IS possible, but it involves a lot more than is shown here. Take a look at the Windows' API VirtualAllocEx() function, along with the ReadProcessMemory() and WriteProcessMemory() functions. I think there is an example here of using these functions to get listview text from another process. The details differ but the principle will be the same for a richtext control.

        Except... although it does not say so in the doc, I think you'd end up allocating memory for the callback function itself in the target processs. Could be fun... once you get past a likely lengthy series of GPFs whilst developing.




        MCM
        Last edited by Michael Mattias; 26 Apr 2009, 01:13 PM.
        Michael Mattias
        Tal Systems (retired)
        Port Washington WI USA
        [email protected]
        http://www.talsystems.com

        Comment


        • #5
          Originally posted by Michael Mattias View Post
          Code:
          >>>>                   CONTROL GET TEXT CB.HNDL, %IDT_hRichEdit TO tRichedit 
                           hRichEdit = VAL("&h"& tRichedit)
          At the point where you send the %EM_STREAMOUT, hRichedit is not handle to the richedit control... it is ... well, it's hard to tell what it is.
          Well, should of pointed out that I was using WinSpy to get the handle to the RichEdit control and pasting that hex value into the %IDT_hRichEdit control, ...
          Furcadia, an interesting online MMORPG in which you can create and program your own content.

          Comment


          • #6
            So hrichedit is the handle returned by program A (WinSpy) which got it from program B (the true target program) which you are trying to use it in program C (your program)?

            Um, yes, you should have pointed this out. It is quite significant. It also explains your GPF.

            As a rule, a handle (window, file, whatever) is only valid in the process in which it is obtained. You can make it valid in another process using the DuplicateHandle() WinAPI function.

            Better I think you should try to get your own handle to the control (see EnumWindows or maybe EnumThreadWindows()and EnumChildWindows functions), and when you find the window of interest (the richedit control), then you can stream it out using the handle you obtained in your enumeration.

            I'm not even sure that will work with the streaming thing, but it would be where I would start were I interested in doing this.

            What was it you wanted to do, exactly? Get the text from a control in a non-cooperating application? Using the enumeration functions above is the way that is generally done.

            MCM
            Michael Mattias
            Tal Systems (retired)
            Port Washington WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              I could see from running your code that the user needed to paste a handle into the textbox but I missed the fact that the handle needed to be in hex - I used Borje's WinSpy which outputs the handle in decimal

              In either case the handle seems to be quite useable without any DuplicateHandle() contortions.

              The problem is that the %EM_STREAMOUT message was not working as hoped. I guessed that it might be possible to (virt)allocate some shared memory in the original app's address space and then work through that but seemed a little over complicated for this task.

              It seems to me that it might be better for you to use the clipboard. Here's some mods to your original example to achieve the same result I think you were after (%SF_RFT to %IDT_RESULTS) - ie the RTF data in 'Markup' form?

              Code:
                              Case %IDB_GETTEXT
                                   Local hRichEdit, msgTextMode    As Dword
                                   Local tRichedit                 As String
                                   Control Get Text Cb.Hndl, %IDT_hRichEdit To tRichedit
                                   hRichEdit = Val("&h"& tRichedit)
                              #Debug Print "Richedit="& Hex$(hRichEdit, 8)
                                  'msgTextMode = SendMessage(hRichEdit, %EM_GETTEXTMODE, 0, 0)
                                  msgTextMode = SendMessage(Val(tRichedit), %EM_GETTEXTMODE, 0, 0)
                              #Debug Print "msgTextMode="& Bin$(msgTextMode)
               
                                    SendMessage hRichEdit, %EM_SETSEL, 0, -1
                                    SendMessage hRichEdit, %WM_COPY, 0, 0
                                    gRichEdit = ClipboardGetText
                                    Control Set Text Cb.Hndl, %IDT_RESULTS, gRichEdit
               
                              Case %IDM_EDIT_COPY
              ..
              ..
              '------------------------------------------------------------------------------
              Function ClipboardGetText() As String
               Local hClipData As Dword 
               Local zPtr As Asciiz Ptr 
               Local TextFormat As Long 
                TextFormat = IIF (IsClipboardFormatAvailable(49517), 49517, %CF_TEXT)  ' "RTF as text" - markup or "CF_TEXT" - plain
                If IsClipboardFormatAvailable(TextFormat) then            ' check if either available on clipboard
                  If IsFalse(OpenClipboard(%NULL)) Then Exit Function
                  hClipData = GetClipboardData(TextFormat)                ' handle to a clipboard object retrieved from Clipboard
                  zPtr = GlobalLock(hClipData)                            ' get pointer to first byte of object's mem block
                  If zPtr <> 0 Then Function = @zPtr                      ' get data starting at first byte 
                  GlobalUnlock hClipData
                  CloseClipboard                                          ' release clipboard
                Else
                  Function = ""
                End If
              End Function
              '------------------/ClipboardGetText
              "49517" is a bit of a 'Magic Number' that translates to "RFT As Text".
              I found it by using the following app to enumerarate the data available on the clipboard following the %WM_COPY command being sent to a RichEdit window I used for tests.
              Code:
              'TT lance
              #Compile Exe
              #Include "WIN32API.INC"
               
              Function ClipboardShowFormats() As String 
               Local lFmt As Long
               Local szTxt As Asciiz * %MAX_PATH
               Local sResult As String
               
                OpenClipboard %NULL
                sResult = "Clipboard has" + Str$(CountClipboardFormats()) + " Formats."
                Do
                  lFmt = EnumClipboardFormats(lFmt)
                  If lFmt Then
                    If IsFalse GetClipboardFormatName(lFmt, szTxt, SizeOf(szTxt)) Then   'format is predefined
                      szTxt = Choose$(lFmt, "CF_Text", "CF_Bitmap", "CF_Metafile", _
                                            "CF_SYLK", "CF_DIF", "CF_TIFF", _
                                            "CF_OEM Text", "CF_DIB", "CF_Palette", _
                                            "CF_PenData", "CF_Riff", "CF_Wave", _
                                            "CF_UnicodeText", "CF_EnhMetafile", "CF_hDrop", _
                                            "CF_Locale", "CF_DIBV5", "CF_Max")
                    End If
                    sResult = sResult + $Cr + Str$(lFmt) +$Tab+ " = " + szTxt
                  End If
                Loop While lFmt
                CloseClipboard
               Function = sResult
              End Function
              '------------------/ClipboardShowFormats
               
              Function PBMain
               
                MsgBox ClipboardShowFormats, , "Using EnumClipboardFormats().."
               
              End Function 
              '------------------/PBMain
              The same registered format is available with content copied from Wordpad too but it has a different numerical value (= 49571) probably need to check on different platforms but the '49517' value seems to be the same in different versions of RichEdit.
              Last edited by Dave Biggs; 28 Apr 2009, 10:34 AM. Reason: Correction - numerical value of registered format "RTF As text"
              Rgds, Dave

              Comment


              • #8
                Correction to post #7

                Corrected post above.
                Had previously stated that the numerical value of the "RTF As text" registered format used with data copied to the clipboard from a RichEdit window was the same as that copied from a Wordpad document. Actually the former is "49517" while the latter is "49571"

                Sorry for the confusion.
                Rgds, Dave

                Comment

                Working...
                X