When you register a window class, you can specify that the background color is a system color by adding one to the system color index, and then casting the result to HBRUSH
:
WNDCLASS wc; ... wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
Why do we add one?
To make sure that the result is not zero.
The COLOR_
system color is index zero. If we didn’t add one to the system color indices, then an attempt to set the background color to the scroll bar color would end up with (HBRUSH)0
, which is a null pointer. When the system saw that you set hbrBackground
to a null pointer, it wouldn’t know whether you meant the window to have no background color (null pointer), or whether you wanted it to have the system scroll bar color (COLOR_
).
In other words, adding one ensures that the space of “system colors smuggled inside a brush handle” does not overlap with the space of regular brush handles.
In retrospect, this was one of those “too clever” hacks born out of the days of 16-bit Windows and systems with only 256KB of memory. Nowadays, we would say, “Just set the background to GetSysColorBrush(COLOR_WINDOW)
.” But back then, GetSysColorBrush(COLOR_WINDOW)
didn’t exist.
GetSysColorBrush says the following:
An application must not register a window class for a window using a system brush. To register a window class with a system color, see the documentation of the hbrBackground member of the WNDCLASS or WNDCLASSEX structures.
And WNDCLASS/WNDCLASSEX doesn't make any mention of GetSysColorBrush() for .hbrBackground.
...
Can we confirm that this is ok?
WNDCLASS wc;
wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
...
It seems strange that it would not be ok - but then what is the GetSysColorBrush warning...
I think this was covered by Raymond way back as well. Normally system brushes aren’t released, but they do when a window class registered with one is unregistered.
At least that’s how i remember it
Indeed, the documentation for the hbrBackground member of WNDCLASSEX (as pointed to above) says as much:
“The system automatically deletes class background brushes when the class is unregistered by using UnregisterClass. An application should not delete these brushes.”
It makes sense that you would not want one of the system brushes to be deleted when your class was unregistered!
The documentation for GetSysColorBrush has the following remark as well:
"Although you don't need to delete the logical brush that GetSysColorBrush returns, no harm occurs by calling DeleteObject."
I'm confused now. So either the documentation wasn't consistently updated as the implementations changed over time, or UnregisterClass does something other than calling DeleteObject on the class background brush. Part of me wants the latter to be true, with a future blog entry detailing "The sad history of UnregisterClass".
It's all straightfoward to me. This is the sequence of events, as I understand it.
WNDCLASS was created first, documented to use the COLOR constant + 1 to represent a system color.
If you pass a brush in to RegisterClass, then UnregisterClass will delete the brush when it unregisters that class. Therefore, YOU shouldn't delete the brush out from under it, it will take care of it for you
GetSysColorBrush() came later, and was documented as...
Man, i’m confused too now 😀 Raymond, can you explain it?
So what is the best practice here? 🙂
WNDCLASS wc;
wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
Is the code that I would want to write… (which avoids the +1/reinterpret_cast nonsense).
But is this “safe” according to the documentation despite the warning that I quoted in my original post?
Raymond also covered this back in 2014: https://devblogs.microsoft.com/oldnewthing/20140305-00/?p=1593
Honestly I wouldn’t mind a “best-of” “classic article” “rerun” featured every day: there’s a lot of treasure buried in the past, what, almost 18 years of content?