What is the DC brush good for?


The DC brush GetStockObject(DC_BRUSH) is a stock brush associated with the device context. Like the system color brushes, the color of the DC brush changes dynamically, but whereas the system color brushes change color based on the system colors, the color of the DC brush changes at your command.

The DC brush is handy when you need a solid color brush for a very short time, since it always exists and doesn't need to be created or destroyed. Normally, you have to create a solid color brush, draw with it, then destroy it. With the DC brush, you set its color and start drawing. But it works only for a short time, because the moment somebody else calls the SetDCBrushColor function on your DC, the DC brush color will be overwritten. In practice, this means that the DC brush color is not trustworthy once you relinquish control to other code. (Note, however, that each DC has its own DC brush color, so you need only worry about somebody on another thread messing with your DC simultaneously, which doesn't happen under any of the painting models I am familiar with.)

The DC brush is amazingly useful when handling the various WM_CTLCOLOR messages. These messages require you to return a brush that will be used to draw the control background. If you need a solid color brush, this usually means creating the solid color brush and caching it for the lifetime of the window, then destroying it when the window is destroyed. (Some people cache the brush in a static variable, which works great until somebody creates two copies of the dialog/window. Then you get a big mess.)

Let's use the DC brush to customize the colors of a static control. The program is not interesting as a program; it's just an illustration of one way you can use the DC brush.

Start, as always, with our scratch program, and making the following changes.

BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
  g_hwndChild = CreateWindow(TEXT("static"), NULL,
        WS_VISIBLE | WS_CHILD, 0, 0, 0, 0,
        hwnd, NULL, g_hinst, 0);
 if (!g_hwndChild) return FALSE;
 return TRUE;
}

HBRUSH OnCtlColor(HWND hwnd, HDC hdc, HWND hwndChild, int type)
{
  FORWARD_WM_CTLCOLORSTATIC(hwnd, hdc, hwndChild, DefWindowProc);
  SetDCBrushColor(hdc, RGB(255,0,0));
  return GetStockBrush(DC_BRUSH);
}

    HANDLE_MSG(hwnd, WM_CTLCOLORSTATIC, OnCtlColor);

Run this program and observe that we changed the background color of the static window to red.

The work happens inside the OnCtlColor function. When asked to customize the colors, we first forward the message to the DefWindowProc function so that the default foreground and background text colors are set. (Not relevant here since we draw no text, but a good thing to do on principle.) Since we want to override the background brush color, we set the DC brush color to red and then return the DC brush as our desired background brush.

The static control then takes the brush we returned (the DC brush) and uses it to draw the background, which draws in red because that's the color we set it to.

Normally, when customizing the background brush, we have to create a brush, return it from the WM_CTLCOLORSTATIC message, then destroy it when the parent window is destroyed. But by using the DC brush, we avoided having to do all that bookkeeping.

There is also a DC pen GetStockObject(DC_PEN) which behaves in an entirely analogous manner.

Comments (21)
  1. Dmitriy says:

    Raymond are you not contradiciting yourself?

    > In practice, this means that the DC brush color is not trustworthy once you relinquish control to other code

    Well, when your code returns you lost control.

    Another words where is the guarantee that control uses your brush right away?

  2. Universalis says:

    The trouble is that Windows doesn’t support the stock DC brush. Only certain specific versions of Windows support it.

    In a corporate context that’s OK because you have some control; but in the wider world you can’t go excluding people from your market like that, so you have to have code that handles the other versions anyway.

  3. Adrian says:

    If every DC has a DC brush, how does GetStockObject(DC_BRUSH) know which one to return? Does it really just a return a magic handle value which the SelectObject call knows about?

  4. Dave says:

    SelectObject(hdc,GetStockObject(DC_BRUSH);

    > SetDCBrushColor(hdc, RGB(00,0xff;00);

    > PatBlt(0,0,200,200,PATCOPY)

    > SetDCBrushColor(hdc,RGB(00,00,0xff);

    > PatBlt(0,0,200,200,PATCOPY);

    Did MSDN run out of closing parens? I will donate a few so the example compiles:

    )))

    :-)

  5. Universalis: True, if you need to run on Windows 98, then you can’t use this.

    Adrian: GetStockObject(DC_BRUSH) returns the DC brush. The DC brush doesn’t have a color itself; it takes its color from the DC. That’s the whole point of the DC brush.

  6. Frank says:

    Will this also work in metafiles?

  7. Mike Dunn says:

    Raymond> I think the question was how does GetStockBrush(DC_BRUSH) know which DC you mean, since there is no HDC parameter?

    Simon Cooke> You bet I’m still supporting Win 98 and Me. The people who buy my software still use 98, and I sure as hell am not going to give up sales (meaning, lose money) by supporting only 2K+.

  8. Universalis says:

    Only about 5% of PC users use Windows XP. Similar proportions for 2000, NT4, and 98.

    The remaining 80% of PC users use Windows.

  9. Simon Cooke says:

    … of course, if you’re still supporting Windows 98 today, 7 years after it came out, I feel for you, I really do.

    Still, at least you’re not supporting Windows ME, right? *evil grin*

  10. asdf says:

    It’s magical in a similar way that monochrome DDBs use the BkColor and TextColor of the DC it’s selected in as their palette as opposed to monochrome DIBs that use a palette stored in the bitmap header. GDI has all sorts of neat tricks like this hidden that not many people seem to know about (another one is that you can get the stock 1×1 monochrome bitmap handle by passing a 0 width or 0 height to certain bitmap creation functions).

  11. Adrian Tyborowski says:

    Unfortunately most of people give up 98 – including Microsoft, Matrox, freeware coders – no support, no drivers, even no apps anymore. I hate situation when I can’t use some software because one feature of XP, which could be, rather, easily implemented or just skipped, as unnecessary, under 98 – either by app’s coder or Microsoft. I.e. GUI+ and some of statistics functions – maybe it’s harder but in my opinion it needs only some exports [at least w/ faked, nearly empty functions] and modifications of blits. I.e. alpha effects could be done by simple, imperfect trick like that one used by Avast!. Then you get uprising compatibility and functionality.

    Hmmm… Maybe it’s time to get my hands on such things :) .

  12. Moi says:

    Adrian, why would Microsoft want to do the harder work to support Windows 98? They would rather you buy a new OS from them, and this is one way they can subtly apply pressure on you to do it. Obsolescence is not a feature of software, but you can build it in if you have a dominany market position.

  13. Moi: If you want to continue supporting Windows 98 then more power to you. It means that you can’t take advantage of features that don’t exist in Windows 98. Or are you saying Windows should never add any new features? Should Windows 1.0 have been the last version of Windows?

  14. Mark Kalmes says:

    This seems like a brittle way to implement WM_CTLCOLORSTATIC. What if you have a custom control that queries WM_CTLCOLORSTATIC and WM_CTLCOLORDLG while drawing? Is it true that the standard Windows controls will always use the brush immediately before sending any other CTLCOLOR messages? (presumably yes)

    It seems strange to force the drawing code to remember that the brushes it gets may become invalidated and change colors at odd times.

  15. Moi says:

    Okay Raymond, and now read my post in the context of Adrian’s post.

  16. What do you suggest should be done to fix it? Should Windows stop adding features so people won’t be tempted to use them?

  17. Moi says:

    I believe Adrian already did this.

  18. Matt Ellis says:

    Raymond, should you call DefWindowProc directly, or should you call it via CallWindowProc?

  19. To position multiple child windows at one go.

  20. To position multiple child windows at one go.

  21. To position multiple child windows at one go.

Comments are closed.