4
votes

According to msdn PrintWindow (retrieved date May 5th 2017)

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.

MSDN never claim about the message WM_PAINT. But what I have tested prove the claim above about the WM_PRINT message wrong.

App A:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_PAINT:
        DefWindowProc(hWnd, message, wParam, lParam);
        break;
    case WM_PRINT:
        OutputDebugStringA("WM_PRINT");
        break;
    case WM_PRINTCLIENT:
        OutputDebugStringA("WM_PRINTCLIENT");
        break;
    //other cases ...
    }
    return 0;
}

App B (more details about App B)

HWND hwnd = FindWindow(NULL, lpString);
//...
//PrintWindow(hwnd, hdc, PW_CLIENTONLY);
PrintWindow(hwnd, hdc, 0);
 

When I call the App B to capture the App A. According to msdn PrintWindow, case WM_PRINT should hit, but instead, case WM_PAINT is hit.

According to this article

If that's true then layered windows not implementing WM_PAINT can't be captured because UpdateWindow just sends WM_PAINT

So at last, I just want to know if msdn is wrong or my code is wrong? PrintWindow send message WM_PAINT or WM_PRINT? If it does really send message WM_PRINT, how does message WM_PRINT works?

2
This MSDN forum entry seems to confirm the issue. Anyway, I would not rely on PrintWindow() or WM_PRINT because they require cooperation from the target window. Instead, simply BitBlt() from the screen DC.zett42
In case user Ðаn stops by: Stop removing appropriate language tags (c++), and add inappropriate compiler tags (visual-c++). Thank you.IInspectable
Maybe in the past, PrintWindow support to send WM_PRINT, but it changed and Microsoft forget to update the article. Maybe here is the reason why it changed: stackoverflow.com/a/830375/4608491123iamking

2 Answers

5
votes

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 Print­Window 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.

0
votes

The windows API PrintWindow uses WM_PAINT for a reason: it works across different processes. A DC handle like other GDI handles is only valid within the same process it was created. Therefore sending WM_PRINT or WM_PRINTCLIENT to a window from another process fails.

But... PrintWindow succeeds in grabbing the content of another process as it first creates a DC in the context of the called process via BeginPaint then copies the content back to the DC of the calling process. Of course they could also stub the WM_PRINT(CLIENT) with another handle but I assume the current solution was simpler.

Btw, the PrintWindow API is introduced in XP it did not yet exists in W2K.