1

I am trying to capture screenshot of inactive window with PrintWindow. It works correctly for calculator and for capturing Google Chrome, but for some other applications, like games, it saves white area.

What could be the reasons for PrintWindow to fail and how to validate them?

EDIT: I want tool to report why window can not be captured

anatoly techtonik
  • 19,847
  • 9
  • 124
  • 140

2 Answers2

4

Every window has a default implementation for WM_PRINT, you will use it if you call PrintWindow() and don't use the PW_CLIENTONLY flag. Provided by the default window procedure, DefWindowProc(). It is pretty straight-forward, it creates a memory DC and sends WM_PAINT. So what you get in the bitmap is the same you get on the screen. Everybody happy.

But that only works for windows that actually use WM_PAINT to render their content. The chips are down when it doesn't, most any game actually renders with DirectX and does so at a high rate, not relying on WM_PAINT messages. A program that uses a layered window with alpha transparency doesn't either, you can typically recognize them from a fancy blended border.

What such a program should do is write their own message handler for WM_PRINT/CLIENT and render their surface into the device context. Necessary because the default implementation doesn't know beans about DirectX surfaces.

Games just don't do this, that's extra code they have to write that just about nobody ever actually uses. You'll inevitably end up with an empty bitmap. Nothing you can do about it of course.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Great insight. Thanks. It appears that capturing Unity windows doesn't work too. So, if DirectX windows don't process WM_PRINT, how to detect them? Is possible to "probe" that window actually processes certain message? How DirectX pixels end up in window frame on desktop - are they already selected into some DC? If yes, maybe it is possible to dump them from there? – anatoly techtonik Jun 07 '15 at 08:59
  • 1
    I'm 98% sure that you cannot reliably detect such a window. There are just no window properties that scream "My content is rendered by DirectX". Only the empty bitmap is a cue. You'll need to build in an option that the user can change, falling back to BitBlt() like the PrintScreen key does. Albeit that it is fairly doomed that the user will always turn on that option because it always works. – Hans Passant Jun 07 '15 at 09:13
  • Nice. That comment fits into the answer to original question. – anatoly techtonik Jun 07 '15 at 09:15
  • `BitBlt` is not guaranteed to work. It can be disabled explicitly through [SetWindowDisplayAffinity](https://msdn.microsoft.com/en-us/library/windows/desktop/dd375340.aspx). – IInspectable Jun 07 '15 at 09:23
  • Not to argue or anything, but how does `WM_PAINT` to a memory DC work? Since standard handling for `WM_PAINT` is to call `BeginPaint` and use the DC returned by that. – Jonathan Potter Jun 07 '15 at 10:46
  • DefWindowProc() does that. I suppose it keeps a flag around that indicates that a PrintWindow is in progress so BeginPaint uses that DC. Not sure. – Hans Passant Jun 07 '15 at 10:52
1

The documentation for PrintWindow provides information on its implementation:

The application that owns the window referenced by hWnd processes the PrintWindow call and renders the image in the device context that is referenced by hdcBlt. The application receives a WM_PRINT message or, if the PW_PRINTCLIENT flag is specified, a WM_PRINTCLIENT message. For more information, see WM_PRINT and WM_PRINTCLIENT.

Whether or not PrintWindow returns a window's content is subject to the window procedure handling the WM_PRINT or WM_PRINTCLIENT message[1] appropriately. If a window doesn't handle either of those messages, it will not render anything into the provided device context.


[1]Standard window implementations provide a message handler for WM_PRINT/WM_PRINTCLIENT through DefWindowProc. Custom window class implementations need to provide their own.
IInspectable
  • 46,945
  • 8
  • 85
  • 181
  • Not that easy, WM_PRINT has a default implementation in DefWindowProc(). The odds it will work in a program that uses DirectX, like a game, that's zero. – Hans Passant Jun 06 '15 at 23:58
  • @Hans: Updated the answer to make it more obvious, that *handling a message* does not necessarily equate to *writing your own implementation*. – IInspectable Jun 07 '15 at 08:25