简体   繁体   English

PrintWindow发送消息WM_PAINT还是WM_PRINT?

[英]PrintWindow send message WM_PAINT or WM_PRINT?

According to msdn PrintWindow (retrieved date May 5th 2017) 根据msdn PrintWindow (2017年5月5日检索日期)

he 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. 拥有hWnd引用的窗口的应用程序处理PrintWindow调用,并在hdcBlt引用的设备上下文中呈现图像。 The application receives a WM_PRINT message or, if the PW_PRINTCLIENT flag is specified, a WM_PRINTCLIENT message. 应用程序接收WM_PRINT消息,或者,如果指定了PW_PRINTCLIENT标志,则接收WM_PRINTCLIENT消息。 For more information, see WM_PRINT and WM_PRINTCLIENT. 有关更多信息,请参阅WM_PRINT和WM_PRINTCLIENT。

MSDN never claim about the message WM_PAINT. MSDN从未声称消息WM_PAINT。 But what I have tested prove the claim above about the WM_PRINT message wrong. 但是我测试的内容证明了上面关于WM_PRINT消息的声明是错误的。

App A: 应用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 ) App B(关于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. 当我打电话给应用程序B捕获应用程序A.根据msdn PrintWindow,案例WM_PRINT应该命中,但相反,案例WM_PAINT被命中。

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 如果这是真的,则无法捕获未实现WM_PAINT的分层窗口,因为UpdateWindow只发送WM_PAINT

So at last, I just want to know if msdn is wrong or my code is wrong? 所以最后,我只是想知道msdn是错还是我的代码错了? PrintWindow send message WM_PAINT or WM_PRINT? PrintWindow发送消息WM_PAINT还是WM_PRINT? If it does really send message WM_PRINT, how does message WM_PRINT works? 如果它真的发送消息WM_PRINT,消息WM_PRINT如何工作?

Simple answer: Yes, I reproduce the behavior you describe on both Windows 10 and Windows XP. 简单回答:是的,我重现了您在Windows 10和Windows XP上描述的行为。 When I call PrintWindow , the target window gets a WM_PAINT message, not a WM_PRINT message. 当我调用PrintWindow ,目标窗口获取WM_PAINT消息, 而不是 WM_PRINT消息。

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). 我不仅可以使用断点和跟踪输出重现它,而且我还可以通过使用调试器逐步执行PrintWindow (隐藏在Windows操作系统内部)来确认它。 Like virtually all User and GDI functions, it is a client-side stub that forwards to the server-side system function NtUserPrintWindow . 与几乎所有User和GDI函数一样,它是一个客户端存根,它转发到服务器端系统函数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 . 从这一点开始,执行后会执行一些系统函数和错误检查,并最终将值15(对应于WM_PAINT消息)加载到EDX寄存器中,然后通过名为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. 这基本上就是PrintWindow所做的全部 - 向指定窗口发送WM_PAINT消息,要求它打印到指定的设备上下文中。 So yes, the MSDN documentation is making an erroneous claim. 所以是的,MSDN文档正在做出错误的声明。 The implementation of PrintWindow does not send a WM_PRINT message. PrintWindow的实现不会发送WM_PRINT消息。

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. 查看ReactOS源代码(Windows操作系统的开源克隆,旨在与二进制API兼容),您可以看到它以不同的方式实现PrintWindow ,但仍然在道德上等同。 (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. (或者,更准确地说,它开始以一种在道德上等同的方式实现PrintWindow 。它的实现似乎是不完整的,并且只返回FALSE 。)在NtUserPrintWindow函数中 ,参数被验证,然后它调用内部函数, IntPrintWindow ,它根据PW_CLIENTONLY标志的规范设置坐标,然后 - 如果它没有提前返回 - 将强制更新窗口并简单地从窗口的DC到指定的DC。

The Wine project (another open-source clone of the Windows APIs) does it differently. Wine项目(Windows API的另一个开源克隆)以不同的方式实现。 There, the PrintWindow function (implemented entirely user-side) simply sends the specified window a WM_PRINT message via SendMessage . 在那里, PrintWindow函数 (完全由用户端实现)只是通过SendMessage向指定的窗口发送WM_PRINT消息。 This was implemented by Luke Benstead in December of 2009 . 这是由Luke Benstead于2009年12月实施的 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. 我的猜测是Luke只是阅读MSDN文档并编写遵循其规范的代码,而不是复制Microsoft操作系统的实际行为。

Now, I had initially assumed that, rather than being completely wrong, MSDN was simply obsolete. 现在,我最初认为MSDN已经过时了,而不是完全错误。 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. 随Windows Vista引入的DWM引发了各种绘图API实现方式的一些变化,我认为PrintWindow的文档仍然指的是传统绘图模型中的工作原理。 (A consequence of documenting implementation details, rather than behavior.) In fact, though, testing on Windows XP disproved this assumption. (记录实现细节而非行为的结果。)事实上,在Windows XP上进行的测试反驳了这一假设。 XP behaves in exactly the same way as Windows 10, with PrintWindow sending a WM_PAINT message and never a WM_PRINT message. XP的行为方式与Windows 10完全相同, PrintWindow发送WM_PAINT消息, PrintWindow发送WM_PRINT消息。 It is possible that the change was made at an even earlier time, and the MSDN documentation is even further out of date. 更改可能在更早的时间进行,并且MSDN文档甚至更新。 For example, perhaps Windows 9x implemented PrintWindow in this way, but NT never did. 例如,也许Windows 9x以这种方式实现了PrintWindow ,但NT从未这样做过。 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: 令我感到奇怪的是, Raymond Chen以与MSDN文档一致的方式传递PrintWindow函数的行为:

The PrintWindow function passes a custom device context as a parameter to the WM_PRINT message… PrintWindow函数将自定义设备上下文作为参数传递给WM_PRINT消息...

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. 这是在2012年左右编写的,他当然可以访问Windows源代码,所以要么我在分析中遗漏了一些内容,要么Raymond 也将他的文章基于官方文档,而不是实际查看实现的内容,因为它并没有真正影响文章的要点。

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. 我无法想象为什么PrintWindow是通过发送WM_PAINT消息还是WM_PRINT消息在内部实现的。 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. 换句话说,App B既不需要知道也不关心如何实现PrintWindow

A correctly-written App A (in other words, all Windows GUI applications) would have handlers for both the WM_PAINT and WM_PRINTCLIENT messages. 正确编写的应用程序A(换句话说,所有Windows GUI应用程序)将具有WM_PAINTWM_PRINTCLIENT消息的处理程序。 WM_PAINT should be handled the obvious way, and WM_PRINTCLIENT should simply piggy-back off of this implementation— eg : 应该以明显的方式处理WM_PAINT ,并且WM_PRINTCLIENT应该简单地背负这个实现 - 例如

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. 您根本没有理由处理WM_PRINT ,因为它由默认窗口过程处理。 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 . 这不仅必须如此(逻辑上),因为此消息的实现必须处理绘制非客户区域,窗口通常不会自行绘制,但MSDN文档明确确认在“备注”部分下DefWindowProc处理此问题消息,根据指定的标志发送窗口相应的子消息,包括WM_ERASEBKGNDWM_PRINTCLIENT

The windows API PrintWindow uses WM_PAINT for a reason: it works across different processes. Windows API PrintWindow使用WM_PAINT是有原因的:它适用于不同的进程。 A DC handle like other GDI handles is only valid within the same process it was created. 像其他GDI句柄一样的DC句柄仅在创建它的同一进程中有效。 Therefore sending WM_PRINT or WM_PRINTCLIENT to a window from another process fails. 因此,将WM_PRINT或WM_PRINTCLIENT发送到另一个进程的窗口失败。

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. 但是...... PrintWindow成功获取另一个进程的内容,因为它首先通过BeginPaint在被调用进程的上下文中创建DC,然后将内容复制回调用进程的DC。 Of course they could also stub the WM_PRINT(CLIENT) with another handle but I assume the current solution was simpler. 当然,他们也可以使用另一个句柄来存根WM_PRINT(CLIENT),但我认为当前的解决方案更简单。

Btw, the PrintWindow API is introduced in XP it did not yet exists in W2K. 顺便说一句,PrintWindow API是在XP中引入的,它在W2K中尚不存在。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM