Why does PrintWindow hate CS_PARENTDC? Because EVERYBODY hates CS_PARENTDC!


Commenter kero wants to know why the Print­Window function hates CS_PARENT­DC. (And CS_CLASS­DC, and CS_OWN­DC.)

Because everybody hates CS_PARENT­DC! (And CS_CLASS­DC, and CS_OWN­DC.)

We saw earlier that these class styles violate widely-held assumptions about how drawing works. I mean, who would have thought that asking for two device contexts would give you the same one back twice? Or that changes to one device context would secretly modify another (because they're really the same)? Or that a window procedure assumes that it will see only one device context ever?

The Print­Window function is really in a pickle when faced with a window with one of these class styles, because the whole point of Print­Window is to render into some other device context. The Print­Window function says "Render into this other device context, please," and the window acts like a bratty two-year-old who refuses to eat food from anything other than his favorite monkey plate. "This is not my monkey plate. I will now throw a tantrum."

The Print­Window function passes a custom device context as a parameter to the WM_PRINT message, and the window says, "Hey, no fair! My class styles say that you aren't allowed to pass any old device context; you have to pass mine. I will now take my broccoli and mash it all over the table."

Oh well. At least it tried.

Yet another reason to avoid these weird class styles.

Comments (16)
  1. KJK::Hyperion says:

    I've only ever seen CS_OWNDC used for console windows, but at least they WM_PRINT ok

  2. Joshua Ganes says:

    I've never had to work with them myself, but I do love the image of a bratty two-year-old kid mashing his broccoli. Add a glob of mashed potatoes flung from a spoon-shaped catapult and you've got a regular slapstick popcorn movie.

  3. David Walker says:

    I didn't know that mashing broccoli had anything to do with programming, but now I know!

  4. Joshua says:

    I used to use CS_CLASSDC to set properties at setup time and leave them (things like persistent brushes that I didn't want to recreate in every WM_PAINT). These days I've learned I can cache the brush and forget about the DC. Hmmm. ParentDC seems like a good idea for a child window and I would hope wouldn't mess up PrintWindow.

  5. Gabe says:

    You described quite well why CS_CLASSDC and CS_OWNDC conflict with PrintWindow (drawing to an arbitrary DC invalidates assumptions about the DC being passed in), but CS_PARENTDC doesn't have the same problem. As I read the documentation, CS_PARENTDC just means the DC for that window will have its clipping region already set to that of its parent window.

    Presumably this means that the DC passed from PrintWindow would differ only in the lack of a clipping region. I would think that this would be fine — indeed, I think it would be preferable. If I wanted a printout of a child window, I would probably want the whole thing, not just the part that was visible within the parent window.

  6. AsmGuru62 says:

    I always wondered what MUST be restored before releasing a DC?

    Only things which were SelectObject-ed or other things too?

    Do these things need to be restored or not:

    • SetBkMode
    • SetTextColor

    • SetTextAlign,

    etc.

  7. MSDN states: " For example, the system uses parent device contexts for push button and edit controls."

    Do dialogs have a problem with  PrintWindow?

  8. Steve says:

    Speaking of WM_PRINT.. What am I supposed to do with those flags passed by lParam when handling WM_PRINTCLIENT? All your examples seem to ignore them. Would it be a good idea to call DefWindowProc (or SendMessage?) with WM_PRINT in the message parameter and only really handle it when lParam is equal to PRF_CLIENT?

    It also seems that many people don't call GetUpdateRect when handling WM_PAINT like it says to in WM_PAINT's documentation. How bad is it if I don't do this? BeginPaint doesn't say anything about it.

  9. Anonymous Coward says:

    KJK::Hyperion: Before the introduction of the HasDC property, the class style for Visual Basic forms* always was CS_OWNDC | DBLCLKS. The second part allows for the Form_DblClick event and doesn't concern us now.

    The CS_OWNDC was specified for two reasons that I know of.

    1) It allows you to store the value in a variable. Even if you don't do this yourself, you may be unwittingly using something that does. For example, if you call OpenGL's wglCreateContext the device context handle is with many video drivers stored somewhere and must always refer to the same window. I believe this wasn't part of the original specification, but since it didn't warn driver programmers about the issue, and they live in a different world, many didn't know better than that the device context they got would always be for the same window. Hence they often didn't call WindowFromDC like they should. So often in fact that this has pretty much become part of the interface, if not on MSDN, at least on OpenGL.org.

    2) It makes it possible for the runtime to, when drawing properties like ForeColor and DrawWidth are set, simply update the device context. If CS_OWNDC had not been present, the runtime library would have to refresh all the drawing properties every time a DC is acquired. (Or make it impossible to set the drawing properties at design time and make it clear that they're lost when the DC is released, which would presumably happen when you return to the message loop, but presumably not when you enter it with DoEvents…)

    ________

    * Form is VB parlance for window (class).

  10. SimonRev says:

    @Steve

    On your second question the answer is pretty easy:  Calling GetUpdateRect and modifying your paint code to accommodate it is, in many cases, premature optimization.  It is much easier to write a paint function that simply assumes the entire window is going to be redrawn — and on modern hardware it is usually fast enough.

  11. Klimax says:

    @Mike Dimmick:

    Windows 7 added back support for some GDI operation, which are now accelerated again. (If driver supports WDDM 1.1)

  12. Mike Dimmick says:

    @Steve: I think they're trying to say that if you're *going* to call GetUpdateRect, you need to do it before calling BeginPaint. BeginPaint copies the current update rectangle into the PAINTSTRUCT's rcPaint member, then validates the update region. Calls to GetUpdateRect after this will not retrieve a valid rectangle, and GetUpdateRgn will return NULLREGION. (Sometimes it speeds up drawing if you only draw the exact pieces that need to be updated, rather than the whole bounding box, so you need to ask for the region rather than the rectangle.)

    @SimonRev: Since Windows Vista, if you have the composited desktop enabled (Aero), GDI painting is done entirely in software, not hardware-accelerated. (However, when something is occluded by another window, it doesn't cause that part of the window to be invalidated – the last image painted is cached as a texture.) It can still be beneficial to optimize painting, to save battery power (CPU/GPU cycles not burned = less energy transferred). It does require that you call InvalidateRect/InvalidateRgn on only the bits that need repainting, of course.

  13. Steve says:

    @SimonRev

    If you look at it as a performance thing I'm not sure it is premature because WM_PAINT is sent when there is nothing to paint. You can still assume everything needs to be redrawn when something needs to be redrawn, as long as that's not nothing. I'm more concerned about it being a correctness issue though. Is the only cost that I will spend a few cycles where I otherwise wouldn't have, or would it be akin to calling BeginPaint/EndPaint outside of WM_PAINT which I guess is another no-no? Is that rule flexible too and BeginPaint is fair game anywhere?

    If I were to guess the answer I would say that you're supposed to call it but lots of people don't read the documentation so there really isn't a penalty other than wasting time, or if there were, they were fixed so they don't happen anymore. But the amount of people who don't do it is so gigantic that it seems like it's less of an issue then that makes it sound. Also it feels like I'm just three or four justifications like that away from being this blog's next butt-head (or at least making a mistake rivaling some of the ones outlined here).

    I keep flipping back and forth between thinking that it's not an issue that should concern anyone anywhere ever, and thinking about how people who do things counter to what the documentation says are treated on here.

    @Mike Dimmick

    That's really not what I get from it. I see it saying that WM_PAINT is sent sometimes when we don't actually need to paint. Besides isn't the update region stored as the HDC's clipping region? Can't you get it from there? Apparently you can't get it from GetClipRgn() for some unexplained reason. GetRandomRgn() seems to do it though.

    I wrote my question about WM_PRINTCLIENT understandably right?

    case WM_PRINTCLIENT:

    ..if (lParam != PRF_CLIENT)

    ….return DefWindowProc(hwnd, WM_PRINT, wParam, lParam);

    ..// handle it

    I kind of want to do this because I don't want to have to print the things described in those flags.. (I also don't know how to.) Is this a bad idea?

  14. Gabe says:

    GetRandomRgn is certainly an interesting name for a function. It would certainly be amusing if it did in fact return a random region! However, it seems that the region is actually totally deterministic. I suspect that GetSpecificRgn would have been a much better name. Although given that SYSRGN is the only documented value that you're supposed to ask for, maybe GetSystemRgn would be better.

    I wonder if there's anybody still at MS who remembers how GetRandomRgn got its name.

  15. kero says:

    Dear Raymond, the meaning of my long-standing question is the following.

    PrintWindow draws CS_PARENTDC-window as a black rectangle.

    But it's easy to fix, if before PrintWindow to remove CS_PARENTDC (+ to restore after).

    So why is this in itself does not provide PrintWindow?

    PrintWindow sometimes spoils the window with the style CS_OWNDC or CS_CLASSDC, – why is not provided to prevent this?

    Finally, although the MSDN says that "This function is similar to WM_PRINT", –

    PrintWindow draws invisible (!WS_VISIBLE) window as black rectangle, but WM_PRINT sees invisible window. Why PrintWindow deprived of this ability?

  16. kero says:

    forgotten (after 2 years have passed):

    PrintWindow sometimes spoils WS_EX_LAYERED (UpdateLayeredWindow)- windows

Comments are closed.