Announcement

Collapse
No announcement yet.

Using WM_PRINT to Capture Screen Contents - and RichEdit...

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

  • Using WM_PRINT to Capture Screen Contents - and RichEdit...

    I am using code from this thread: http://www.powerbasic.com/support/pb...ad.php?t=24583

    to capture the contents of the screen - since they can be obscured by either sizing limitations on the users computer - or the taskbar, etc. Wanted to try and make sure all the contents of the window are there no matter what.

    I have hit a snag - It seems that WM_PRINT only gets the non-client portion of the RichEdit control to print to the destination device context. Anybody have any idea why?

    Any suggestions as to a possible workaround?

    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
    #IF NOT %DEF(%WINAPI)
        #INCLUDE "WIN32API.INC"
    #ENDIF
    #IF NOT %DEF(%RICHEDIT_INC)
        #INCLUDE "RICHEDIT.INC"
    #ENDIF
    #INCLUDE "PBForms.INC"
    #PBFORMS END INCLUDES
    '-----------------------------------------------------------------------------------------------------------------
    
    '-----------------------------------------------------------------------------------------------------------------
    '   ** Constants **
    '-----------------------------------------------------------------------------------------------------------------
    #PBFORMS BEGIN CONSTANTS
    %IDD_DIALOG_CAPTURETEST =  101
    %IDC_RICHEDIT1          = 1001
    %IDC_BUTTON1            = 1002
    %IDC_BUTTON2            = 1003
    %IDC_BUTTON3            = 1004
    %IDC_BUTTON4            = 1005
    %IDC_BUTTON_CAPTURE     = 1006
    #PBFORMS END CONSTANTS
    '-----------------------------------------------------------------------------------------------------------------
    
    '-----------------------------------------------------------------------------------------------------------------
    '   ** Declarations **
    '-----------------------------------------------------------------------------------------------------------------
    DECLARE CALLBACK FUNCTION ShowDIALOG_CAPTURETESTProc()
    DECLARE FUNCTION ShowDIALOG_CAPTURETEST(BYVAL hParent AS DWORD) AS LONG
    #PBFORMS DECLARATIONS
    '-----------------------------------------------------------------------------------------------------------------
    
    '-----------------------------------------------------------------------------------------------------------------
    '   ** Main Application Entry Point **
    '-----------------------------------------------------------------------------------------------------------------
    FUNCTION PBMAIN()
        PBFormsRichEdit ()      ' Load RichEdit
    
        ShowDIALOG_CAPTURETEST %HWND_DESKTOP
    
        PBFormsRichEdit (%TRUE) ' Unload RichEdit
    END FUNCTION
    '-----------------------------------------------------------------------------------------------------------------
    
    '-----------------------------------------------------------------------------------------------------------------
    
    ' hWnd, form or control to print to file.
    ' sFileName, file to save to, if "" the hDIB (hBITMAP) handle is returned
    
    FUNCTION FormToFile( BYVAL hWnd AS LONG, BYVAL sFileName AS STRING ) AS LONG
    
        DIM FF                  AS LONG
        DIM hDC                 AS LONG
        DIM hMemDC              AS LONG
        DIM hDIB                AS LONG
        DIM hOldDIB             AS LONG
        DIM bm                  AS BITMAP
        DIM bmi                 AS BITMAPINFO
        DIM lpBITMAPFILEHEADER  AS BITMAPFILEHEADER
        DIM lpBITMAPINFOHEADER  AS BITMAPINFOHEADER
        DIM PA                  AS POINTAPI
        DIM PA1                 AS POINTAPI
        DIM R                   AS Rect
    
        IF hWnd = 0 THEN EXIT FUNCTION
        IF IsWindow( hWnd ) = 0 THEN EXIT FUNCTION
    
        '// Measure window
        GetWindowRect   hWnd, R
        ClientToScreen  hWnd, PA
    
        '// Create a memory dc.
        hDC     = GetDC( hWnd )
        hMemDC  = CreateCompatibleDC( hDC )
    
        '// Make it a DIB.
        bmi.bmiHeader.biSize        = SIZEOF( bmi.bmiHeader )
        bmi.bmiHeader.biWidth       = ( R.nRight - R.nLeft )
        bmi.bmiHeader.biHeight      = ( R.nBottom - R.nTop )
        bmi.bmiHeader.biPlanes      = 1
        bmi.bmiHeader.biBitCount    = 24
        bmi.bmiHeader.biCompression = %BI_RGB
    
        hDIB = CreateDIBSection( hMemDC, bmi, %DIB_RGB_COLORS, 0, 0, 0 )
        hOldDIB = SelectObject( hMemDC, hDIB )
    
        RedrawWindow hWnd, BYVAL 0&, BYVAL 0&, %RDW_INTERNALPAINT OR %RDW_UPDATENOW OR %RDW_ALLCHILDREN
    
        '// Print the non-client area first.
        SendMessage hWnd, %WM_PRINT, hMemDC, %PRF_NONCLIENT
    
        '// Print the client area using an offset.
        OffsetWindowOrgEx hMemDC, R.nLeft - PA.x, R.nTop - PA.y, PA1
        SendMessage hWnd, %WM_PRINT, hMemDC, %PRF_CHILDREN OR %PRF_CLIENT OR %PRF_OWNED OR %PRF_ERASEBKGND
        OffsetWindowOrgEx hMemDC, PA1.x, PA1.y, BYVAL 0&
    
        GetObject hDIB, SIZEOF( bm ), bm
    
        lpBITMAPFILEHEADER.bfType = CVI("BM")
        lpBITMAPFILEHEADER.bfSize = LEN(lpBITMAPFILEHEADER) + LEN(lpBITMAPINFOHEADER) + bm.bmWidthBytes * bm.bmHeight
        lpBITMAPFILEHEADER.bfOffBits = 54
    
        lpBITMAPINFOHEADER.biSize = 40
        lpBITMAPINFOHEADER.biWidth = bm.bmWidth
        lpBITMAPINFOHEADER.biHeight = bm.bmHeight
        lpBITMAPINFOHEADER.biPlanes = 1
        lpBITMAPINFOHEADER.biBitCount = 24
        lpBITMAPINFOHEADER.biSizeImage = 54& + bm.bmWidthBytes * bm.bmHeight
    
        IF sFileName > "" THEN
    
            FF = FREEFILE
            OPEN sFileName FOR OUTPUT AS #FF
            PRINT #FF, lpBITMAPFILEHEADER;
            PRINT #FF, lpBITMAPINFOHEADER;
            PRINT #FF, PEEK$( bm.bmBits, bm.bmWidthBytes * bm.bmHeight );
            CLOSE #FF
    
        END IF
    
        SelectObject hMemDC, hOldDIB
    
        ReleaseDC hWnd, hDC
        DeleteDC hMemDC
    
        IF sFileName > "" THEN
            DeleteObject hDIB
        ELSE
            FUNCTION = hDIB
        END IF
    
    END FUNCTION
    
    '-----------------------------------------------------------------------------------------------------------------
    '   ** CallBacks **
    '-----------------------------------------------------------------------------------------------------------------
    CALLBACK FUNCTION ShowDIALOG_CAPTURETESTProc()
    
        SELECT CASE AS LONG CBMSG
            CASE %WM_INITDIALOG
                ' Initialization handler
    
            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 %IDC_RICHEDIT1
    
                    CASE %IDC_BUTTON1
                        IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                            MSGBOX "%IDC_BUTTON1=" + FORMAT$(%IDC_BUTTON1), %MB_TASKMODAL
                        END IF
    
                    CASE %IDC_BUTTON2
                        IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                            MSGBOX "%IDC_BUTTON2=" + FORMAT$(%IDC_BUTTON2), %MB_TASKMODAL
                        END IF
    
                    CASE %IDC_BUTTON3
                        IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                            MSGBOX "%IDC_BUTTON3=" + FORMAT$(%IDC_BUTTON3), %MB_TASKMODAL
                        END IF
    
                    CASE %IDC_BUTTON4
                        IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                            MSGBOX "%IDC_BUTTON4=" + FORMAT$(%IDC_BUTTON4), %MB_TASKMODAL
                        END IF
    
                    CASE %IDC_BUTTON_CAPTURE
                        IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                            FormToFile CBHNDL, "C:\CAPTURETEST.BMP"
                        END IF
    
                END SELECT
        END SELECT
    END FUNCTION
    '-----------------------------------------------------------------------------------------------------------------
    
    '-----------------------------------------------------------------------------------------------------------------
    '   ** Dialogs **
    '-----------------------------------------------------------------------------------------------------------------
    FUNCTION ShowDIALOG_CAPTURETEST(BYVAL hParent AS DWORD) AS LONG
        LOCAL lRslt AS LONG
    
    #PBFORMS BEGIN DIALOG %IDD_DIALOG_CAPTURETEST->->
        LOCAL hDlg  AS DWORD
    
        DIALOG NEW hParent, "WM_PRINT Capture Test", 70, 70, 298, 228, %WS_POPUP OR %WS_BORDER OR %WS_DLGFRAME OR _
            %WS_SYSMENU OR %WS_MINIMIZEBOX OR %WS_CLIPSIBLINGS 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
        CONTROL ADD PBFormsRichEdit(), hDlg, %IDC_RICHEDIT1, "RichEdit1", 5, 5, 290, 135, %WS_CHILD OR %WS_VISIBLE _
            OR %WS_TABSTOP OR %WS_VSCROLL OR %WS_HSCROLL OR %ES_LEFT OR %ES_MULTILINE OR %ES_AUTOVSCROLL OR _
            %ES_AUTOHSCROLL OR %ES_WANTRETURN, %WS_EX_CLIENTEDGE OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR _
            %WS_EX_RIGHTSCROLLBAR
        CONTROL ADD BUTTON, hDlg, %IDC_BUTTON1, "Button1", 5, 140, 50, 15
        CONTROL ADD BUTTON, hDlg, %IDC_BUTTON2, "Button2", 5, 155, 50, 15
        CONTROL ADD BUTTON, hDlg, %IDC_BUTTON3, "Button3", 5, 170, 50, 15
        CONTROL ADD BUTTON, hDlg, %IDC_BUTTON4, "Button4", 5, 185, 50, 15
        CONTROL ADD BUTTON, hDlg, %IDC_BUTTON_CAPTURE, "&Capture", 245, 210, 50, 15
    #PBFORMS END DIALOG
    
        DIALOG SHOW MODAL hDlg, CALL ShowDIALOG_CAPTURETESTProc TO lRslt
    
    #PBFORMS BEGIN CLEANUP %IDD_DIALOG_CAPTURETEST
    #PBFORMS END CLEANUP
    
        FUNCTION = lRslt
    END FUNCTION
    '-----------------------------------------------------------------------------------------------------------------
    Adam Drake
    PowerBASIC

  • #2
    Yes, it's an odd thing isn't?

    On Jose's website there was an examle shown using a different approach.
    Try that.

    In short, it obtained the windowDC and then obtained the bitmap (handle).
    hellobasic

    Comment


    • #3
      I've tried searching some - but no luck yet - is it on the forum, or his main website?
      Adam Drake
      PowerBASIC

      Comment


      • #4


        However, the save part is not in.
        Shouldn't be to hard..
        hellobasic

        Comment


        • #5
          That's some cool code there - but what it seems to be doing is just taking a snapshot of the entire desktop - I'm still in need of something that can capture window contents that are not totally in view (possibly due to scrolling out of view)...Which aside from RichEdit works great with the code above - do you know of a possible alternative? Or am I just out of luck with RichEdit?
          Adam Drake
          PowerBASIC

          Comment


          • #6
            The problem, with WM_PRINT and WM_PRINTCLIENT, is that many applications or custom child controls do not handle correctly these messages, and thus do not paint themselves into the provided wParam memory DC.
            Last edited by Patrice Terrier; 5 Feb 2009, 03:13 PM.
            Patrice Terrier
            www.zapsolution.com
            www.objreader.com
            Addons: GDImage.DLL 32/64-bit (Graphic library), WinLIFT.DLL 32/64-bit (Skin Engine).

            Comment


            • #7
              Starting to figure that one out

              Do you know of some other way to capture a windows contents if it is obscured by something?
              Adam Drake
              PowerBASIC

              Comment


              • #8
                Perhaps changing the z-order using SetWindowPos.

                The other solution is to use trampolin or hooking the Beginpaint Endpaint API.
                But it can only be done from a DLL, and it will not work on Windows x64.
                Last edited by Patrice Terrier; 5 Feb 2009, 04:00 PM.
                Patrice Terrier
                www.zapsolution.com
                www.objreader.com
                Addons: GDImage.DLL 32/64-bit (Graphic library), WinLIFT.DLL 32/64-bit (Skin Engine).

                Comment


                • #9
                  For windows 'out of scope' you may try to use the DC via their GetWindowDC() but i have little hope.
                  WM_PRINT stuff forces to paint to a DC but this new technique not.

                  Do not delete the DC then but use ReleaseDC()

                  --
                  >but what it seems to be doing is just taking a snapshot of the entire desktop ..

                  Note that you may need bitblit the contents for saving anyway, you can easily calculate what area of the full DISPLAY dc must be copied into the memoryDC.
                  hellobasic

                  Comment


                  • #10
                    Patrice, not sure I communicated clearly what I meant by obscured - basically not even visible because of scrolling...not hidden because of Z Order...

                    I thank you both for your direction - though I found a solution that seemed to work, and thought I'd share...At least for the RichEdit control (I only use single line ones in this app) - creative usage of EM_FORMATRANGE actually seemed to do the trick (though not thoroughly tested yet):

                    Code:
                            CASE %WM_PRINTCLIENT
                    
                                GetClientRect hWnd, rc
                                GetWindowRect hWnd, tmprc
                    
                                FillRect wParam, rc, GetSysColorBrush(%COLOR_WINDOW)
                                TYPE SET tmprc2 = rc
                    
                                MapWindowPoints hWnd, %HWND_DESKTOP, rc, 2
                    
                                fr.rc.nTop       = ((rc.nTop - tmprc.nTop) / 2) * 15
                                fr.rcPage.nTop   = fr.rc.nTop
                    
                                fr.rc.nLeft     = ((rc.nLeft - tmprc.nLeft) / 2) * 15
                                fr.rcPage.nLeft = fr.rc.nLeft
                    
                                fr.rc.nRight         = tmprc2.nRight * 15
                                fr.rcPage.nRight     = fr.rc.nRight
                    
                                fr.rc.nBottom        = tmprc2.nBottom * 15
                                fr.rcPage.nBottom    = fr.rc.nBottom
                    
                                fr.hdc           = wParam
                                fr.hdcTarget     = wParam
                                fr.chrg.cpMin    =  0
                                fr.chrg.cpMax    = -1&
                    
                                SendMessage(hWnd, %EM_FORMATRANGE, 1&, VARPTR(fr))
                    
                                SendMessage hWnd, %EM_FORMATRANGE, 1, %NULL
                    
                                EXIT FUNCTION
                    Adam Drake
                    PowerBASIC

                    Comment


                    • #11
                      I encountered myself a nasty redrawing problem with the RichEdit control in my "PhotoComposer" project to display help text, when hidden behind another control. The behavior of the hidden RichEdit was not the same on XP and VISTA.

                      To solve this, i had to use something like this:
                      Code:
                      IF IsWindowVisible(zGetMainItem(%ID_RICHEDIT)) THEN
                          CALL zPutFocusOn(zGetMainItem(%ID_RICHEDIT))
                          CALL zKeyBd_Event(%VK_CONTROL, %VK_HOME)
                      END IF
                      and to load the help file matching a specific language i had to use this:
                      Code:
                      %WS_VSCROLL                                      = &H00200000
                      %WS_TABSTOP                                      = &H00010000
                      %ES_MULTILINE                                    = &H4&
                      %ES_AUTOVSCROLL                                  = &H40&
                      %ES_AUTOHSCROLL                                  = &H80&
                      %ES_READONLY                                     = &H800&
                      %EM_LIMITTEXT                                    = &HC5
                      %EM_STREAMIN                                     = %WM_USER + 73
                      %EM_STREAMOUT                                    = %WM_USER + 74
                      '%EM_AUTOURLDETECT                                = %WM_USER + 91
                      %SF_RTF                                          = &H0002
                      
                      TYPE EDITSTREAM DWORD
                        dwCookie AS DWORD    ' user value passed to callback as first parameter
                        dwError AS DWORD     ' last error
                        pfnCallback AS DWORD
                      END TYPE
                      
                      FUNCTION RichLoad_Callback(BYVAL dwCookie AS DWORD, BYVAL pbbuff AS BYTE PTR, BYVAL cb AS LONG, BYREF pcb AS LONG) AS LONG
                          LOCAL psBuffer AS STRING PTR
                          psBuffer = dwCookie
                          pcb = MIN&(LEN(@psBuffer), cb)
                          IF pcb > 0 THEN
                              POKE$ pbBuff, LEFT$(@psBuffer, pcb)
                              @psBuffer = MID$(@psBuffer, pcb + 1)
                          END IF
                      END FUNCTION
                      
                      FUNCTION RichLoadFromFile(BYVAL hWndRTF AS LONG, zFileName AS ASCIIZ) AS LONG
                          LOCAL ES AS EDITSTREAM
                          LOCAL hFile AS LONG
                          LOCAL sBuffer AS STRING
                          IF zsExist(zFileName) THEN
                             CALL zsFOpen(zFileName, 0, 2, hFile)
                             sBuffer = STRING$(zsFlof(hFile), 0)
                             CALL zsFGet(hFile, sBuffer)
                             CALL zsFClose(hFile)
                             es.dwCookie = VARPTR(sBuffer)
                             es.pfnCallback = CODEPTR(RichLoad_Callback)
                             FUNCTION = SendMessage(hWndRTF, %EM_STREAMIN, %SF_RTF, VARPTR(es))
                          END IF
                      END FUNCTION
                      Patrice Terrier
                      www.zapsolution.com
                      www.objreader.com
                      Addons: GDImage.DLL 32/64-bit (Graphic library), WinLIFT.DLL 32/64-bit (Skin Engine).

                      Comment

                      Working...
                      X