Simple answer: Yes, I reproduce the behavior you describe on both Windows 10 and Windows XP. When I call PrintWindow
, the target window gets a WM_PAINT
message, not a WM_PRINT
message.
Not only can I reproduce it using breakpoints and trace output, but I can also confirm it by using the debugger to step through the implementation of PrintWindow
(buried inside of the Windows operating system itself). Like virtually all User and GDI functions, it is a client-side stub that forwards to the server-side system function NtUserPrintWindow
. From this point, execution follows through some more system functions and error checks, and ultimately loads the value 15 (which corresponds to the WM_PAINT
message) into the EDX
register, and then dispatches this message via an internal function called DispatchClientMessage
.
This is essentially all that PrintWindow
does—sends the specified window a WM_PAINT
message, asking it to print into the specified device context. So yes, the MSDN documentation is making an erroneous claim. The implementation of PrintWindow
does not send a WM_PRINT
message.
Looking at the ReactOS source code (an open-source clone of the Windows operating system that aims to be binary API-compatible), you can see that it implements PrintWindow
a bit differently, but still in a way that is morally equivalent. (Or, more precisely, it begins implementing PrintWindow
in a way that is morally equivalent. Its implementation appears to be incomplete, and simply returns FALSE
.) In its NtUserPrintWindow
function, the parameters are verified, and then it calls down to an internal function, IntPrintWindow
, which sets up the coordinates based on the specification of the PW_CLIENTONLY
flag, and then—if it didn't return early—would force an update of the window and simply blit from the window's DC to the specified DC.
The Wine project (another open-source clone of the Windows APIs) does it differently. There, the PrintWindow
function (implemented entirely user-side) simply sends the specified window a WM_PRINT
message via SendMessage
. This was implemented by Luke Benstead in December of 2009. My guess would be that Luke simply read the MSDN documentation and wrote code that followed its specification, rather than copying the actual behavior of Microsoft's OS.
Now, I had initially assumed that, rather than being completely wrong, MSDN was simply obsolete. The DWM, introduced with Windows Vista, prompted a number of changes in the way that various drawing APIs were implemented, and I assumed that the documentation for PrintWindow
was still referring to how things worked in the legacy drawing model. (A consequence of documenting implementation details, rather than behavior.) In fact, though, testing on Windows XP disproved this assumption. XP behaves in exactly the same way as Windows 10, with PrintWindow
sending a WM_PAINT
message and never a WM_PRINT
message. It is possible that the change was made at an even earlier time, and the MSDN documentation is even further out of date. For example, perhaps Windows 9x implemented PrintWindow
in this way, but NT never did. I don't have access to such a system with a compiler at the moment, so I can't verify. If I remember, I'll update this answer later.
What's strange to me is that Raymond Chen described in passing the behavior of the PrintWindow
function in a manner consistent with the MSDN documentation:
The PrintWindow
function passes a custom device context as a parameter to the WM_PRINT
message…
This was written some time around 2012, and he certainly has access to the Windows source code, so either I am missing something in my analysis, or Raymond also based his article on the official documentation instead of actually looking to see what the implementation does, since it doesn't really affect the main point of the article.
Speaking of which, the thing that I don't really understand about your question is why any of this matters. Sure, it's fun to study the way the OS actually works, but you shouldn't write code based on what you find when reverse-engineering. I can't imagine any reason why it would matter whether PrintWindow
was implemented internally by sending a WM_PAINT
message or a WM_PRINT
message. In a sane version of either, the effect would be the same: you'd get the requested portion of the specified window drawn into the specified device context. Simple as that. In other words, App B neither needs to know nor care how PrintWindow
is implemented.
A correctly-written App A (in other words, all Windows GUI applications) would have handlers for both the WM_PAINT
and WM_PRINTCLIENT
messages. WM_PAINT
should be handled the obvious way, and WM_PRINTCLIENT
should simply piggy-back off of this implementation—e.g.:
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
OnPaintContent(ps);
EndPaint(hWnd, &ps);
return 0;
}
case WM_PRINTCLIENT:
{
PAINTSTRUCT ps;
ps.hdc = reinterpret_cast<HDC>(wParam);
GetClientRect(hWnd, &ps.rcPaint);
OnPaintContent(ps);
return 0;
}
...
void PaintContent(const PAINTSTRUCT& ps)
{
// Paint the window's content here.
}
There is no reason for you to handle WM_PRINT
at all, since that's handled by the default window procedure. Not only must this be so (logically), since the implementation of this message must handle painting the non-client area, which a window does not normally paint itself, but the MSDN documentation explicitly confirms under the "Remarks" section that DefWindowProc
handles this message, sending the window the appropriate sub-messages, according to the specified flags, including WM_ERASEBKGND
and WM_PRINTCLIENT
.
PrintWindow()
orWM_PRINT
because they require cooperation from the target window. Instead, simplyBitBlt()
from the screen DC. – zett42SetWindowDisplayAffinity
makes this a little harder to acheive: How do I make it more difficult for somebody to take a screenshot of my window? – Remy Lebeau