6

My goal is to take screenshots off an application while the laptop screen is off, but instead the screenshot will always be the same as just before turning off the screen. It does not redraw itself once the screen is off, and remains frozen.

I'm obtaining a screenshot using printwindow from Python (using method described here: Python Screenshot of inactive window PrintWindow + win32gui

This method works nicely as long as I have my laptop screen on, but if I turn it off, it simply returns the last image before the screen turned off. I've tried using win32gui.RedrawWindow, hoping that this would force a redraw, but I haven't gotten it to work, even trying all the different flags. I've also tried getting screenshots via pyautogui, but this also has the same problem. Is there any way to redraw the application while the laptop screen is off?

Ruehri
  • 828
  • 2
  • 11
  • 21
  • Did you find a solution for this? I am having the same issue... – Ruehri Dec 01 '18 at 10:04
  • Unfortunately I have not. – Nulinspiratie Dec 02 '18 at 22:33
  • The above example takes screen shot of *Calculator* which is a UWP app in Windows 10. `PrintWindow` won't work with screen on or off. Another code works on *"Steam"* which I assume is not a native Win32 application either, and thus runs in to the same problem. The code also seems to have resource leaks, it just stops working after a while. I am not sure if the problem is necessarily the screen being off. Hit the "Print Screen" key on the keyboard while the screen is off, then view the clipboard content with *"Paint"* and see if it printed the screen or not. Not same as sleep mode? – Barmak Shemirani Dec 27 '18 at 06:40
  • I do not think it has to do with resource leaks as it happens instantly after turning off the screen. You can easily reproduce it with `SendMessage(win32con.HWND_BROADCAST, win32con.WM_SYSCOMMAND, SC_MONITORPOWER, 2)` where `SC_MONITORPOWER = 0xF170` – Ruehri Dec 28 '18 at 09:54
  • I can't reproduce that on my desktop. Maybe there is something special about laptops or some graphics cards. – Barmak Shemirani Dec 28 '18 at 21:10
  • I'm doing the following: 1. Find a window that has moving content (I use the android emulator Memu running an app), something with a builtin clock works 2. Run a continuous loop that grabs the window and saves the screenshot to a file, with timestamp (e.g., take a screenshot every second with filename=timestamp) 3. Run the SendMessage command (The screen should go out immediately) 4. What I can observe is that before turning off the screen, the screenshots are always in sync with the window, the moment the screen is turned off they stop updating and turning the screen back on resyncs it – Ruehri Dec 29 '18 at 10:43
  • Side-note: I tested it on both my desktop (Nvidia GPU) and laptop (Also Nvidia GPU). Same result on both machines. – Ruehri Dec 29 '18 at 11:07
  • Emulator is unlikely to work with this method at all. You are using some code which we cannot see. Most likely you are taking screen shot of desktop dc which should always be available with screen on or off. – Barmak Shemirani Dec 30 '18 at 16:35

1 Answers1

6

It would be nice if there were a straightforward way to do what you want to do, but unfortunately it's just not possible without some convoluted low-level work.

In the underlying WinAPI, native windows redraw themselves only when they receive WM_PAINT messages, and they only receive WM_PAINT messages if they are visible. They don't get the messages if they're behind another window, or if they're dragged off screen, or minimized, or their visibility is set to False. They also aren't visible when the screen is off. Since they aren't visible, they simply never get told to redraw themselves. That's why the screen capture image doesn't change after the screen is turned off.

There is no command you can issue that will override this -- it is implemented as a fundamental part of the windowing paradigm.

One solution is to have the application paint itself in something that's not a window (e.g., a dialog box) and capture that rather than capturing the screen. You could also modify the application by overriding OnPaint and using a timer to invalidate and call your OnPaint function periodically (since WM_PAINT messages won't be received).

If it were me I'd override OnPaint, make my application draw itself to a background context bitmap rather than the normal window, then use a timer to invalidate and redraw periodically and just take the bitmap of the background context whenever I wanted to capture the output.

If you don't own the code for the applications that are running, you may still be able to get applications to redraw themselves by running everything in a virtual machine. I've done that successfully for headless computing in the past, but it was many OS versions ago and things may be different now, so YMMV...

Information on manual screenshots under VMWare is here.

Information on programmatic screenshots under Hyper-V is here.

Craig.Feied
  • 2,617
  • 2
  • 16
  • 25