Grabbing a screenshot of a background window does not work

Windows specific forum
User avatar
Kurzer
Enthusiast
Enthusiast
Posts: 666
Joined: Sun Jun 11, 2006 12:07 am
Location: Near Hamburg

Grabbing a screenshot of a background window does not work

Post by Kurzer »

Hello,

the following code should find a windows handle by its window title. Then the program get the DC from this window via GetWindowDC_(hWnd). After this the programm copies a part of the window bitmap of the determined window into a image and display it in a window.

So far so good, but my problem is, that the BitBlt_() function grabs simply what is seen on Desktop. That means, if the desired window is overlaped by another window, then the bitmap of the other windows ist still part of the screenshot. It looks like a 'desktop-screenshot'.

On Windows 7 this works like expected. There I get a screenshot of the desired window, even if it's complete in background and overlapped by other windows.

This drives me crazy. I have no idea what is the reason for the failure on Windows XP SP3 - 32 bits.

Does anyone have an idea?

Code: Select all

EnableExplicit

Structure GrabWindow
   sWindowTitle.s
   iWindowHandle.i
EndStructure

Global stBrowserWindow.GrabWindow
Global.i iWindow, iCanvas_Screenshot

Procedure.i Settings_FindWindowHandleEnumProc(iWindowHandle.i, lParameter.l)
   ; +-----------------------------------------------------------------
   ; |Description  : EnumProceduere für Settings_GetWindowHandle()
   ; |Arguments    : lWindowHandle: Enthält das Windowhandle des aktuell enumerierten Fensters
   ; |             : lParameter   : Muss die Speicheradresse der Parameter-Struktur enthalten
   ; |Results      : 0, wenn die enumeration gestoppt werden soll, sonst 1
   ; |Remarks      : Siehe auch: https://msdn.microsoft.com/de-de/library/windows/desktop/ms633497(v=vs.85).aspx
   ; +-----------------------------------------------------------------
   Protected.s sTitle=Space(32)
   
   GetWindowText_(iWindowHandle, sTitle, 32) 
   
   If FindString(sTitle, stBrowserWindow\sWindowTitle) <> 0 
      stBrowserWindow\iWindowHandle = iWindowHandle
      ; Das gesuchte Fenster wurde gefunden, EnumProc nicht weiter aufrufen
      ProcedureReturn 0
   Else 
      ; Weitere Fenster durchsuchen, EnumProc weiter aufrufen
      ProcedureReturn 1
   EndIf 
EndProcedure
Procedure.l Settings_GetWindowHandle(sWindowTitle.s)
   ; +-----------------------------------------------------------------
   ; |Description  : Ermittelt das Windowhandle eines Fensters anhand des Fenstertitels
   ; |Arguments    : sWindowTitle: Titel des zu suchenden Fensters
   ; |Results      : Das gesuchte Windowhandle oder 0, wenn das Fenster nicht gefunden wurde
   ; |Remarks      : Es reicht aus, wenn der linke Teil des Fenstertitels angegeben wird.
   ; |             : Z.B.: "PureBasi" -< findet auch das Fenster mit dem Titel "PureBasic 5.50 (x86)"
   ; +-----------------------------------------------------------------
   
   stBrowserWindow\sWindowTitle = sWindowTitle
   
   If EnumWindows_(@Settings_FindWindowHandleEnumProc(), @stBrowserWindow) = 1
      ProcedureReturn 0
   Else
      ProcedureReturn stBrowserWindow\iWindowHandle
   EndIf
EndProcedure
Procedure   Grab(sWindowTitle.s)
   ; +-----------------------------------------------------------------
   ; |Description  : -
   ; |Arguments    : -
   ; |Results      : -
   ; |Remarks      : -
   ; +-----------------------------------------------------------------
   Protected.i iBrowserWindowHandle, iBrowserDC, iCanvasDC
   
   iBrowserWindowHandle = Settings_GetWindowHandle(sWindowTitle)
   If iBrowserWindowHandle > 0
      iCanvasDC = StartDrawing(CanvasOutput(iCanvas_Screenshot))
      If iCanvasDC
         iBrowserDC = GetWindowDC_(iBrowserWindowHandle)
         If iBrowserDC > 0
            BitBlt_(iCanvasDC, 0, 0, 300, 300, iBrowserDC, 0, 0, #SRCCOPY)
            ReleaseDC_(iBrowserWindowHandle, iBrowserDC)
         EndIf
         StopDrawing()
      EndIf
   EndIf
EndProcedure

iWindow = OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, 320, 320, "", #PB_Window_SystemMenu|#PB_Window_TitleBar)
If iWindow
   iCanvas_Screenshot = CanvasGadget(#PB_Any, 10, 10, 300, 300)
   Grab("Neuer Tab")  ; <<<---- Type in here the titel of the desired Window
EndIf

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
It looks like this on my Win XP machine:

Image
Last edited by Kurzer on Tue Sep 27, 2016 4:09 pm, edited 1 time in total.
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2024: 56y
"Happiness is a pet." | "Never run a changing system!"
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Doing a screenshot of a background window does not work

Post by IdeasVacuum »

Does it work if the target window is not a browser window?
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8435
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Doing a screenshot of a background window does not work

Post by netmaestro »

There is a Bitblt flag called "CAPTUREBLT" which, if combined with SRCCOPY, will include covering windows. Since the version of Windows you're using is quite old, I just wonder how long that flag has been in existence and if its functionality might have been the default at one time. If that turns out to be the case, an alternative might be the way to go. Give this a try and see if it works or not:

Code: Select all

np = RunProgram("notepad.exe",#PB_Compiler_Home+"SDK\readme.txt", GetCurrentDirectory(),#PB_Program_Open)
Delay(300)
notepad = FindWindow_(0, "Readme.txt - Notepad")

ca = RunProgram("calc.exe","","",#PB_Program_Open)
Delay(300)
calc = FindWindow_(0, "Calculator")
GetWindowRect_(calc, @wc.RECT)
GetWindowRect_(notepad, @wr.RECT)
MoveWindow_(calc, wr\left+128, wr\top+128,wc\right-wc\left,wc\bottom-wc\top, #True)
Delay(2000)

Prototype Printwindow(hwnd, hdc, flags)
OpenLibrary(0, "user32.dll")
Printwindow_.PrintWindow = GetFunction(0, "PrintWindow")

dc = CreateDC_("DISPLAY",0,0,0) ; Note: You can't use the hDC returned from StartDrawing()
hdc = CreateCompatibleDC_(dc)   ; for the PrintWindow API. 

CreateImage(0, wr\right-wr\left, wr\bottom-wr\top, 24)
SelectObject_(hdc, ImageID(0))
If hdc
  Printwindow_(notepad, hdc, 0)
EndIf 
DeleteDC_(dc)
DeleteDC_(hdc)
CloseLibrary(0)

OpenWindow(0,0,0,wr\right-wr\left,wr\bottom-wr\top,"",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
ImageGadget(0,0,0,0,0,ImageID(0))
Repeat:Until WaitWindowEvent()=#PB_Event_CloseWindow

KillProgram(np)
KillProgram(ca)
CloseProgram(np)
CloseProgram(ca)
BERESHEIT
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: Doing a screenshot of a background window does not work

Post by freak »

In the past, invisible window parts were not actually rendered to save resources. The reason this works for you in Windows 7 is because of desktop composition where each window is rendered to an off-screen image for later compositing. If you switch off desktop composition on Windows 7 it will probably stop working as well.

What you should do is create an Image device context and then send #WM_PRINT to the target window to tell it to draw its content to your image.
quidquid Latine dictum sit altum videtur
User avatar
Kurzer
Enthusiast
Enthusiast
Posts: 666
Joined: Sun Jun 11, 2006 12:07 am
Location: Near Hamburg

Re: Doing a screenshot of a background window does not work

Post by Kurzer »

Thank you folks for your enlightening insights into screencapturing.
IdeasVacuum wrote:Does it work if the target window is not a browser window?
No, same behavior with all kinds of windows.

@netmaestro, thank you very much for your sniplet. This one works with "PrintWindow_", a GDI command, wich was also mentioned by RSBasic in the german forum (I asked also in the german forum for a solution). PrintWindow_ works for me in all cases, but it captures the entire window. To get a small part of the window, I have to capture it into a big image and from there I have to grab the small rect at the desired x/y coordinate into a second image.

Would this be the correct way to grab a specified rect from another window?

PS: #CAPTUREBLT is not known on Windows XP SP3

PS2: You wrote the following remark: "Note: You can't use the hDC returned from StartDrawing()"
Why I cant use this DC? If I try the following code, the captured Window will be displayed in the CanvasGadget. Whats wrong with this approach?

Code: Select all

iBrowserWindowHandle = FindWindow_(0, "Rechner")
iCanvasDC = StartDrawing(CanvasOutput(Window_Settings_Canvas_Screenshot))
Printwindow_(iBrowserWindowHandle, iCanvasDC, 0)
StopDrawing()
@freak, thank you too for the explanation why it works with BitBlt_() on Windows 7. I am not very familiar with API, GDI and DeviceContext stuff, so I was unfortunately not able to write testcode from your suggestion with #WM_PRINT.
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2024: 56y
"Happiness is a pet." | "Never run a changing system!"
Post Reply