Page 1 of 1

Capture image from any window

Posted: Sun Jun 24, 2007 9:14 pm
by Joakim Christiansen
It shows a list of your windows and let you capture screenshots from them. It works perfectly in Windows Vista, but in Windows XP there is an error; it will include other windows in the screenshot if they're over the window you capture... (Could anyone please help me fix this?)

Code: Select all

EnableExplicit

Procedure CaptureImage(hWnd=#False)
  Protected hImage, hdc, ClientRect.RECT
  
  If hWnd
    GetClientRect_(hWnd,@ClientRect)
    With ClientRect
      If \bottom > 0
        hImage = CreateImage(#PB_Any,\right,\bottom)
        hdc = StartDrawing(ImageOutput(hImage))
          BitBlt_(hdc,0,0,\right,\bottom,GetDC_(hWnd),0,0,#SRCCOPY)
          ;ReleaseDC_()
        StopDrawing()
      EndIf
    EndWith
  Else
    ;Capture desktop
  EndIf
  ;GetDCEx_(hWnd,ClientRect,
  ;#DCX_INTERSECTUPDATE|#DCX_VALIDATE
  ProcedureReturn hImage
EndProcedure

; Procedure ImageWindow(hWnd);,Width,Height)
; 
; 
; EndProcedure 

Enumeration ;Windows
  #Main
  #Image
EndEnumeration
Enumeration ;Gadgets
  #Main_WindowList
  #Main_Capture
  #Main_Close
  #Image_Image
EndEnumeration

;Debug 
Structure Image
  ID.l
  Width.l
  Height.l
EndStructure
Global WindowCount, Image.Image, hWnd, Title.s{128}

Procedure EnumWindows(hWnd,lParam)
  Protected Title.s{128}, windowIsOwned, windowStyle, NormalWindow
  
  SendMessage_(hWnd,#WM_GETTEXT,128,@Title)
  
  windowIsOwned = GetWindow_(hWnd,#GW_OWNER) <> 0
  windowStyle = GetWindowLong_(hWnd,#GWL_EXSTYLE)
  NormalWindow = #True
  
  ;Do not allow invisible processes.  
  If Not IsWindowVisible_(hWnd)
    NormalWindow = #False
  EndIf
  
  ;Window must have a caption
  If Title = ""
    NormalWindow = #False
  EndIf
  
  ;Should not have a parent
  If GetParent_(hWnd) <> 0
    NormalWindow = #False
  EndIf
  
;   ;Don't allow unowned tool tips
;   If (windowStyle | #WS_EX_TOOLWINDOW) <> 0 And Not windowIsOwned
;     NormalWindow = #False
;   EndIf
;   
;   ;Don't allow applications with owners
;   If (windowStyle | #WS_EX_APPWINDOW) = 0 And windowIsOwned
;     NormalWindow = #False
;   EndIf
  
  If NormalWindow
    AddGadgetItem(#Main_WindowList,WindowCount,Title)
    SetGadgetItemData(#Main_WindowList,WindowCount,hWnd)
    WindowCount + 1
  EndIf
  
  ProcedureReturn #True
EndProcedure

OpenWindow(#Main,0,0,250,200,"Select window to capture",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
CreateGadgetList(WindowID(#Main))
ListViewGadget(#Main_WindowList,5,5,240,165)
ButtonGadget(#Main_Capture,5,175,100,20,"Capture")
ButtonGadget(#Main_Close,145,175,100,20,"Close")

EnumWindows_(@EnumWindows(),#Null)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Select EventWindow()
        Case #Main
          Break
        Case #Image
          CloseWindow(#Image)
      EndSelect
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #Main_Capture
          If GetGadgetState(#Main_WindowList) > -1
            hWnd = GetGadgetItemData(#Main_WindowList,GetGadgetState(#Main_WindowList))
            Image\ID     = CaptureImage(hWnd)
            If Image\ID
              Image\Width  = ImageWidth(Image\ID)
              Image\Height = ImageHeight(Image\ID)
              GetWindowText_(hWnd,@Title,128)
              OpenWindow(#Image,0,0,Image\Width,Image\Height,"Screenshot - "+Title,#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
              CreateGadgetList(WindowID(#Image))
              ImageGadget(#Image_Image,0,0,Image\Width,Image\Height,ImageID(Image\ID))
            Else
              MessageRequester("Error","Unable to capture image, window may be minimized...",#MB_ICONINFORMATION)
            EndIf
          EndIf
        Case #Main_Close
          Break
      EndSelect
  EndSelect
ForEver

Re: Capture image from any window

Posted: Sun Jun 24, 2007 10:02 pm
by PB
> Procedure CaptureImage(hWnd=#False)
> Protected hImage, hdc, ClientRect.RECT
> If hWnd

The "If" above is pointless for a start, because you're always going to passing
a valid hWnd to the procedure, aren't you? I think you should change it to:

If IsWindow_(hWnd)

to see if the window is still "alive" at the time the procedure is called.

Re: Capture image from any window

Posted: Sun Jun 24, 2007 10:20 pm
by Joakim Christiansen
Actually I was going to make the procedure so it captures the desktop if you pass an empty hWnd to it. But yes I agree that IsWindow_(hWnd) is smart to check...

Posted: Sun Jun 24, 2007 11:59 pm
by netmaestro
in Windows XP there is an error; it will include other windows in the screenshot if they're over the window you capture... (Could anyone please help me fix this?)
You can capture the client area of any window whose handle you know, using this tecnique:

Code: Select all

GetClientRect_(windowhandle, @cr.RECT)
CreateImage(0, cr\right-cr\left, cr\bottom-cr\top, 24)
hdc=StartDrawing(ImageOutput(0)) 
  SendMessage_(windowhandle, #WM_PRINT, hdc, #PRF_CLIENT|#PRF_CHILDREN|#PRF_ERASEBKGND|#PRF_CHECKVISIBLE )
StopDrawing() 
Done this way, it doesn't matter what other windows are obscuring it, it only draws its own client area to the image. Basically, the message is asking the window to redraw itself to the specified dc instead of its own.

Posted: Mon Jun 25, 2007 1:59 am
by Joakim Christiansen
Just tried that now by changing this line:

Code: Select all

BitBlt_(hdc,0,0,\right,\bottom,GetDC_(hWnd),0,0,#SRCCOPY)
into this:

Code: Select all

SendMessage_(hWnd,#WM_PRINT,hdc,#PRF_CLIENT|#PRF_CHILDREN|#PRF_ERASEBKGND|#PRF_CHECKVISIBLE)
Can't say it gave me good results... (At least not in Vista where I tried it...)