What went wrong in Windows 95 if you use a system color brush as your background brush?


If you want to register a window class and use a system color as its background color, you set the hbrBackground member to the desired color, plus one, cast to an HBRUSH:

wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

Windows 95 introduced "system color brushes", which are a magic type of brush which always paint in the corresponding system color, even if the system color changes.

HBRUSH hbrWindow = GetSysColorBrush(COLOR_WINDOW);

The hbrWindow brush will always paint in the color corresponding to GetSysColor(COLOR_WINDOW), even if the user changes the color scheme later.

Now, you might be tempted to use a system color brush as your class background brush. After all, if you want the background to be the system window color, why not use a brush that is always the system window color?

Well, because the documentation for GetSysColorBrush explicitly tells you not to do that. If you tried this on Windows 95, it would have seemed to work for a while, and then bad things would start happening.

The system color brushes were added as a convenience to save people the trouble of having to create solid color brushes just to draw a system color. Profiling in Windows 95 revealed that a lot of time was spent by applications creating solid brushes, using them briefly, and then destroying them, so anything that could be done to reduce the rate at which applications needed to do this was a good thing.

The system color brushes were implemented in Windows 95 by creating them and then telling GDI, "Hey, if somebody tries to DeleteObject this brush, don't let them." This prevented the system color brushes from being accidentally destroyed.

Except when it didn't.

When you registered a window class with a background brush (and by that, I mean an honest-to-goodness brush and not a pseudo-brush you get from that (HBRUSH)(COLOR_xxx + 1) stuff) the window manager did the same thing to the brush as it did to the system color brushes: It told GDI, "Hey, if somebody tries to DeleteObject this brush, don't let them." This prevented people from destroying brushes while the window manager was still using them to draw the background of a window.

When you unregistered the window class, the window manager told GDI, "Okay, delete this brush, and yes, I told you not to let anyone DeleteObject it, but I'm overriding it because I was the one who protected it in the first place. I'm just cancelling my previous instructions to protect this brush." The window manager takes responsibility for destroying the brush when you register the class; therefore, when you unregister the class, the window manager is obligated to clean up and destroy the brush. (Actually, it's a little more complicated than that, because it is legal to use one brush as the background brush for multiple window classes, but let's ignore that case for now.)

Do you see the problem yet?

What if you registered a window class with a system color brush as the background brush and then unregistered it? (Don't forget that classes are automatically unregistered when the process exits.) When you registered the class, the brush got protected, and when you unregistered the class, the Windows 95 window manager told GDI to override the protection and destroy the brush anyway.

Oops, we just destroyed a system color brush. Even though those brushes were protected, the protection didn't work here because you went through the code path where the window manager said, "Override the safety interlocks!"

But of course, you didn't need to use a system color brush in the first place. You should have used that (HBRUSH)(COLOR_xxx + 1) pseudo-brush.

(Note to nitpickers: This story talked about Windows 95. It does not apply to any other version of Windows. The problem may or may not exist in those other versions; I make no claims.)

Comments (24)
  1. Bob says:

    So what happened when people went down this code path? Palette corruption? Random crashes?

    For that matter, what tipped you guys off that people were doing this?

  2. Peter Ritchie says:

    What happens?  Now they’re using a brush handle that in the best case is just "freed", or it has been re-used as a different colour brush.  Worst case, it’s not a brush handle anymore and who knows what will happen in that case…

  3. Frederik Slijkerman says:

    It would have been better if the Win95 developers would have introduced a new ‘system’ flag for brushes instead of re-using the ‘do not delete because I am a class brush’ flag…

  4. JS says:

    I love you, .NET.

  5. Aaron says:

    re: Frederik

    Yeah, on casual analysis this just looks like bad design.  Don’t implement an optimization and then break that very same optimization by implementing something that unregisters/destroys/whatever the handle regardless.  Couldn’t the code check to see if the handle was some sort of special "immortal" handle and not tear it down in that case?  Where these two features implemented by different groups or something?

  6. GregM says:

    I take it that "don’t destroy this" was just a flag and not a reference count.

  7. ping? says:

    Why wasn’t it done with a reference count in the first place?…

  8. Mike says:

    Isn’t hindsight wonderful?  Obviously what happened was that originally there wasn’t (and as far as I know still isn’t) any support for reference counted brushes.  Nothing wrong with that, it wasn’t deemed necessary in the original design.

    So then along comes sombody who notices this perf issue.  The GDI guys already have a mechanism to prevent "system" brushes from being deleted so it’s a trivial feature to implement.  They either don’t remember or don’t know that the USER guys (a different team remember) are already monkeying around with this flag on thier own whim and boom you have a bug.

  9. Gabe says:

    Keep in mind this was 1995. Back then programmers were expected to cooperate with the system, and documenting what not to do was considered sufficient. That’s laughable now in the days where users must be protected from programmers, but it wasn’t then.

  10. Kuwanger says:

    “Keep in mind this was 1995. Back then programmers were expected to
    cooperate with the system, and documenting what not to do was
    considered sufficient.”

    I disagree.  While it was the case that many people were used
    to the idea, having moved over from Win16, and it was assumed that
    programmers weren’t nefarious, it wasn’t the case that it was assumed
    documentation would be a sufficient protection.

    Simply put, programs crashed because of bugs.  One of the main
    selling points of Windows 95, over Windows 3.11, was a greatly
    increased resistance to program crashes causing system-wide failures
    (along, of course, with a 32-bit api).  Yet competitors of the
    time (OS/2 and Windows NT being good examples) implemented a truly
    separate memory address space (ie, they didn’t, AFAIK, leave critical
    shared DLLs in a read/writeable share arena where any program could do
    nasty things effecting all running programs) and worked to resolve
    misbehaving programs from causing the system to crash or other programs
    running at the same time to malfunction in unexpected (for the user)
    ways; ie, a programmer gotcha wouldn’t be covered and programs that did
    malfunction would almost certainly rapidly crash themselves alone.

    Having said all that, this was clearly a bug and an oversight in the
    original design.  Whoever designed it clearly wasn’t thinking far
    enough ahead to actual usage.  Certainly such is a common affair
    in programming (sadly), but that doesn’t really excuse in any way this
    instance.  In any case, it sounds like old news and an interesting
    example of how a simple design change many times need more thought put
    into it.

    [System color brushes were a new Windows 95
    feature. I thought you folks decided that people who mis-use new
    features deserve what they get. -Raymond
    ]
  11. waleri says:

    For me is much more interesting, why the value is +1

    [You should be able to figure this out on your own. -Raymond]
  12. Kuwanger says:

    "System color brushes were a new Windows 95 feature. I thought you folks decided that people who mis-use new features deserve what they get."

    Perhaps I’m not understanding the effects of destroying the system color brushes, but does that have a system-wide effect?  If so, I’d like to point out that memory protection was also a new Windows 95 feature.  It’d seem appropriate to make either the system color brushes read-only to processes or give each process their own copy.

    If, however, system color brushes are local to a process only and the effect is merely one application trashing itself, then I guess I don’t see it as much of a big deal.  It’d probably help if I were to study the MSDN before spouting off, eh?

  13. 640k says:

    [You should be able to figure this out on your own. -Raymond]

    Does that reply mean:

    1. It’s obvious and too time consuming for you to explain.
    2. You don’t know.

    3. A polite way to tell people to use the suggestion box instead.

    4. You really think all readers are able to figure this out on their own.

    PS I’m not able to figure this out on my own.

  14. Stu says:

    So what you’re saying, Raymond, is that there is a bug in Windows 95’s handling of system colour brushes in conjunction with Window backgrounds?

    So why wasn’t it fixed!? I can understand keeping bug-compatability with older versions of Windows, but this was a new feature!

    [What’s to fix? Programs that encountered this problem were breaking the rules. They deserved to lose. -Raymond]
  15. Neal says:

    I haven’t done any programming in 5 years, and did very little gdi stuff when I did, but I’ll take a guess at it.  COLOR_XXX’s are defined starting at 0, hence the first would give you a null brush, which is either not a good thing or it has a special meaning (believe this is the case) therefore the +1.

  16. Chris says:

    640k,

    Check out the COLOR_XXXX #defines in winuser.h, especially COLOR_SCROLLBAR

  17. Norman Diamond says:

    > The (HBRUSH)(COLOR_xxx+1) thing existed since

    > Windows 1.0 and always created a tracking

    > pseudo-brush.

    I thought this description in the base note

    > Windows 95 introduced “system color brushes”,

    > which are a magic type of brush which always

    > paint in the corresponding system color, even

    > if the system color changes.

    meant that the older type didn’t magically track system colour changes.

    [The (HBRUSH)(COLOR_xxx+1) pseudo-brushes always
    tracked. GetSysColorBrush provides real-brush equivalents to those
    pseudo-brushes (which work only in window class background
    registration). -Raymond
    ]
  18. Norman Diamond says:

    > But of course, you didn’t need to use a

    > system color brush in the first place. You

    > should have used that (HBRUSH)(COLOR_xxx + 1)

    > pseudo-brush.

    But of course you[*] DID need to use a system colour brush in the first place.  When you discovered that you had to use that (HBRUSH)(COLOR_xxx + 1) pseudo-brush instead, you lost the following capability:

    > a magic type of brush which always paint in

    > the corresponding system color, even if the

    > system color changes

    Pedantic correction to my own writing:  you[*] didn’t lose that capability because

    > it seemed to work for a while, and then bad

    > things would start happening.

    So you[*] never had that capability in the first place, you lost a misguided hope of having that capability.  Still, I see how the idea sure looked attractive to programmers of the day, just as you[**] did.

    [* “you” = impersonal 2nd-person developer not Mr. Chen]

    [** “you” = Mr. Chen, just to add an off-topic token abuse of an ambiguous grammar]

    [I don’t know what you’re talking about. The (HBRUSH)(COLOR_xxx+1) thing existed since Windows 1.0 and always created a tracking pseudo-brush. -Raymond]
  19. Jon says:

    a bit offtopic, but would you care to comment about the vista shutdown menu discussion?

    [If you’ve been following this site you’d know
    that is not something I write about. (What’s the title of this blog
    again?) All I’m going to say is that there’s more to it than people
    make it out to be. All you’ve seen so far is one side of the story.
    -Raymond
    ]
  20. KJK::Hyperion says:

    Jon, I will comment if it’s the same to you. That article is full of shit. Windows gives exactly two options, and they are the right ones, you know, those Joel pleads for, and I don’t understand what the hell the article is even about. Maybe about that "advanced" pop-up menu nobody concerned will ever see

    The new "sleep mode" that debuts in Vista is also the "right choice": it works as stand-by, i.e. instant power-on, but behind the scenes it also performs a hibernate, so when the power goes off or 20 minutes pass, the computer safely turns off and your session is preserved

  21. Stu says:

    “[What’s to fix? Programs that encountered this problem were breaking the rules. They deserved to lose. -Raymond]”

    But in this case, seeing as it is a new feature and all, the “rules” were probably written around the bug.

    [It was designed that way on purpose. The
    assumption is that programmers aren’t stupid. Programming is hard
    because nobody said it was going to be easy. Before commenting further
    please read this article. I get the feeling most of the people saying that it’s a bug don’t understand the mindset of the day. -Raymond
    ]
  22. 0x0d says:

    nice example how bug turns into feature, when nobody want fix it :)

  23. Norman Diamond says:

    If you want Windows NT you know where to get

    it.

    On its own that statement is true, but it’s not a valid answer to complaints about Windows 95.  People who wanted to pay for Windows NT instead of paying for Windows 95 (or 98) didn’t know where to get it.  Unless we could make do with slyly configured crippled selections of hardware from about two vendors, we had to pay twice in order to get Windows NT.

  24. 0x0d says:

    /*It was designed that way on purpose. The assumption is that programmers aren’t stupid.*/

    But note that win’95 pretended to be multitasking OS. So it should at least _try_ to protect programs from being corrupted by closing handles from other programs. So this is a bug in this protection.

    [If you want Windows NT you know where to get it. -Raymond]

Comments are closed.