What does the CS_OWNDC class style do?

Recall that window DCs are most commonly used only temporarily.
If you need to draw into a window, you call
BeginPaint
or, if outside of a paint cycle, GetDC,
although painting outside of a paint cycle is generally to be avoided.
The window manager produces a DC for the window and returns it.
You use the DC, then restore it to its original state and return
it to the window manager with
EndPaint (or ReleaseDC).
Internally, the window manager keeps a small cache of DCs which it
dips into when people come asking for a window DC,
and when the DC is returned, it goes back into the cache.
Since window DCs are used only temporarily, the number of outstanding DCs
is typically not more than a handful, and a small cache is sufficient
to satisfy DC demands in a normally-running system.

If you register a window class and include the CS_OWNDC
flag in the class styles,
then the window manager creates a DC for the window and puts it into
the DC cache with a special tag that means
"Do not purge this DC from the DC cache
because it's the CS_OWNDC for this window."
If you call BeginPaint or GetDC to get
a DC for a CS_OWNDC window,
then that DC will always be found and returned
(since it was marked as "never purge").
The consequences of this are good, bad, and worse.

The good part is that since the DC has been created specially for
the window and is never purged,
you don't have to worry about "cleaning up the DC" before
returning it to the cache.
Whenever you call BeginPaint or GetDC
for a CS_OWNDC window,
you always get that special DC back.
Indeed, that's the whole point of CS_OWNDC windows:
You can create a CS_OWNDC window,
get its DC, set it up the way you like it
(selecting fonts, setting colors, etc.), and even if you
release the DC and get it again later, you will get that
same DC back and it will be just the way you left it.

The bad part is that you're taking something that was meant to be
used only temporarily (a window DC) and using it permanently.
Early versions of Windows had a very low limit for DCs (eight or so),
so it was crucial that DCs be released as soon as they weren't needed.
That limit has since been raised significantly, but the underlying
principle remains: DCs should not be allocated carelessly.
You may have noticed that the implementation of CS_OWNDC
still uses the DC cache; it's just that those DCs get a special marking
so the DC manager knows to treat them specially.
This means that a large number of CS_OWNDC DCs end up
"polluting" the DC cache, slowing down future calls to
functions like BeginPaint and ReleaseDC
that need to search through the DC cache.

(Why wasn't the DC manager optimized to handle the case of
a large number of CS_OWNDC DCs?
First, as I already noted, the original DC manager didn't have
to worry about the case of a large number of DCs since the system
simply couldn't even create that many in the first place.
Second, even after the limit on the number of DCs was raised,
there wasn't much point in rewriting the DC manager to optimize
the handling of CS_OWNDC DCs since programmers were
already told to use CS_OWNDC sparingly.
This is one of the practicalities of software engineering:
You can do only so much.
Everything you decide to do comes at the expense of something else.
It's hard to justify optimizing a scenario that programmers were told
to avoid and which they in fact were already avoiding.
You don't optimize for the case where somebody is abusing your system.
It's like spending time designing a car's engine so it maintained
good gas mileage when the car has no oil.)

The worse part is that most windowing framework libraries and
nearly all sample code
assume that your windows are not CS_OWNDC windows.
Consider the following code that draws text in two fonts,
using the first font to guide the placement of characters in the second.
It looks perfectly fine, doesn't it?

We get two DCs for the window.
In the first we select our first font; in the second, we select the second.
In the first DC, we also set the text alignment to TA_UPDATECP
which means that the coordinates passed to the TextOut
function will be ignored.
Instead the text will be drawn starting at the "current position"
and the "current position" will be updated to the end of the string,
so that the next call to TextOut will resume where the
previous one left off.

Once the two DCs are set up, we draw our string one character at a time.
We query the first DC for the current position and draw the character
in the second font at that same x-coordinate (but a bit lower),
then we draw the character in the first font (which also advances the
current position).

After the text drawing loop is done, we restore the states of the two
DCs as part of the standard bookkeeping.

The intent of the function is to draw something like this,
where the first font is bigger than the second.

H

e

l

l

o

H

e

l

l

o

And if the window is not CS_OWNDC that's what you get.
You can try it out by calling it from our scratch program:

But if the window is CS_OWNDC, then bad things happen.
Try it yourself by changing the line
wc.style = 0;
to
wc.style = CS_OWNDC;
You get the following unexpected output:

HHeelllloo

Of course, if you understand how CS_OWNDC works,
this is hardly unexpected at all.
The key to understanding is remembering that when the window
is CS_OWNDC then GetDC just returns the
same DC back no matter how many times you call it.
Now all you have to do is walk through the
FunnyDraw function remembering that
hdc1 and hdc2 are in fact
the same thing.

Now this loop completely falls apart.
At the first iteration, we retrieve the current position from
the DC, which returns (0, 0) since we haven't moved it yet.
We then draw the letter "H" at position (0, 30) into the
second DC.
But since the second DC is the same as the first one, what
really happens is that we are calling TextOut into
a DC that is in TA_UPDATECP mode.
Thus, the coordinates are ignored, the letter "H" is displayed
(in the second font), and the current position is updated to
be after the "H".
Finally, we draw the "H" into the first DC (which is the same as
the second).
We think we're drawing it with the first font, but in fact
we're drawing with the second font.
We think we're drawing at (0, 0), but in fact we're
drawing at (x, 0), where x is the width of
the letter "H",
because the call to TextOut(hdc2, ...)
updated the current position.

Thus, each time through the loop, the next character in the
string is displayed twice, all in the second font.

But wait, the disaster isn't finished yet.
Look at our cleanup code:

SelectFont(hdc1, hfPrev1);

This restores the original font into the DC.

SelectFont(hdc2, hfPrev2);

This re-selects the first font!
We failed to restore the DC to its original state
and ended up putting a "corrupted" DC into the cache.

That's why I described CS_OWNDC as "worse".
It takes code that used to work and breaks it
by violating assumptions that most people make (usually
without realizing it) about DCs.

And you thought CS_OWNDC was bad.
Next time I'll talk about the disaster that is known as
CS_CLASSDC.

It’s hard to justify optimizing a scenario that programmers were
told to avoid and which they in fact were already avoiding.

Except for us suckers trying to write OpenGL apps, eh?

[I don’t follow. I was referring to intentional violating the
guidelines for how a particular feature should be used. Presumably,
OpenGL has similar guidelines on how it should and shouldn’t be used.
(Not being an OpenGL programmer I don’t know for sure. I’m thinking
guidelines like “Don’t allocate unnecessary XYZ’s.”) -Raymond]

Little bit of trivia: the only part of Windows that uses CS_OWNDC exclusively are the console windows (go on, have a look with Spy++), altough I’m not exactly sure why (I suppose to allow drawing from outside the window’s thread? or maybe for performance?). It’s one of the many ways console windows are "special"

“but the underlying principle remains: DCs should not be allocated carelessly”

(I don’t claim to know much about Windows API programming).

When I was first learning about how to program the Windows API on Win 3.1 I took this advice to heart and always freed my DCs as soon as possible. This made sense considering all the memory limits in Windows 3.1.

Now, Raymond, who clearly knows what he’s talking about, says “but the underlying principle remains: DCs should not be allocated carelessly”. In today’s virtual memory based Windows, why is this true? In other words, why are DCs, and the memory they require, more critical than any other kind of Windows API data structure? Why can’t they be allocated and managed dynamically? On a system with free memory why can’t you have a gazillion DCs?

(Again, I’m asking because I’d like to know. I’m not questioning anybody’s knowledge).

Semi-off-topic (i.e. not the point you were aiming for) but there’s
another good reason to *always* release resources in the opposite order
you acquired them. Although it wouldn’t fix the printing problem, the
DC would at least have been returned in it’s original state if the
cleanup was written as:

SelectFont(hdc2, hfPrev2);

ReleaseDC(hwnd, hdc2);

SetTextAlign(hdc1, taPrev1);

SelectFont(hdc1, hfPrev1);

ReleaseDC(hwnd, hdc1);

[Actually I did that on purpose specifically because it caused
the “dirty DC” problem. A naive reading would say that we did clean up
in reverse order – if you look at it on a per-DC basis. (Well, okay, I
restored text alignment out of order.) -Raymond]

Wow, like the others, I’m surprised by the ability to use multiple DC’s… That could have made life easier for sure.

SaveDC and RestoreDC were mentioned already… I’m surprised those aren’t more widely used as well. When I first found those, it was a hallelujah moment. Why bother keeping track of old objects yourself, making your code bulkier, more complex and potentially introduce bugs, when you can just reset it they way it was with one call before leaving a function. It’s stack-based too.

I
think one point Flounder makes that’s a good one: using CClientDC
rather than GetDC. Obviously it’s a dumb mistake to not call
ReleaseDC, but….people make mistakes. I’d rather have scope
handle the dirty work. Your posted “FunnyDraw” method would be a
few lines shorter, and more resiliant to changes by People Who Don’t
Know Better (TM).

If you didn’t want to use MFC, it’d be pretty trivial to make a small wrapper class that did the same thing as CClientDC

[I think you missed the point of the article. CClientDC wouldn’t have helped any. -Raymond]

Most sample code that I’ve seen doesn’t bother to restore the DC to its original state (bitmaps, pens, fonts, etc) prior to releasing it. They just assume that Windows will "reset" the DC when it’s no longer being used by anyone, just like everything else.

Ah yes, CS_OWNDC. I used to work at a software company which produced massive VB6-based applications. Virtually *all* of the app’s windows had CS_OWNDC (at least on NT–we didn’t support our app on 9x). And people wondered why our app was reaching the limit of 10000 GDI handle per process…

PatriotB — VB 6 Forms and UserControls have a property called HasDC which sets the CS_OWNDC class bit when set to true. Of course it defaults to true as well instead of false. I can remember going through this issue for one of our apps that would absolutely smash the GDI heap limits on Win 9x. This was in a large system with a large number of VB custom controls used on a large number of forms.

I never figured out why they defaulted it to true. It seems to be wrong. If you are doing something specific that requires the class bit, you should know enough to turn it on…

[A
wrapper class is just a special case of a class library. And it
wouldn’t have helped here anyway. I guess I’m not sure what your point
is. Yes, “FunnyDraw” would have been a few lines shorter with a wrapper
class, but it would be just as buggy. -Raymond]

Suppose you are writing a graphics intensive application, like a photo editing app or a game, wouldn’t that be a good time to use CS_OWNDC? As long as you don’t try to get multiple DCs for your window, you should be fine, right? Or am I missing something?

Also, I thought one of the benefits of CS_OWNDC was that you can get the DC once after you created the window and then use it throughout your application, without further calls to GetDC or BeginPaint. That’s pretty much what the documentation says. Is that correct?

“[You should allocate anything carelessly. DCs are a subset of “anything”. -Raymond]”

I’ll presume that Raymond accidently left out the word “not” from this. But, the fact remains that there’s something unusual about DCs even in a virtual memory environment. It’s obvious that nothing should be allocated carelessly but I get the impression that it would be worse if I allocated 10,000 DCs than 10,000 buffers containing the string “The Old New Thing”. Right? Why?

If a DC isn’t cleaned up before being released, then a corrupted DC
goes into a per-thread cache. Therefore it seems likely that all
released DCs, corrupted or not, go into a per-thread cache.

When do DCs become cross-process objects? When they’re
acquired they become cross-process but when they’re released they go
into a per-thread cache? (I sure hope not, I sure hope this
logical inference is as illogical to you as it is to me.) Do
CS_OWNDC windows do something to turn their DCs into cross-process
objects?

And Reinder’s question still seems relevant, if DCs are cross-process objects.

[Well, some experimentation tells me that DCs are not cross-process after all (they were in Windows 3.1), so I apologize for the misinformation. I am not a substitute for formal documentation.

By “cross-process” I meant that a DC handle, once created, is valid in any process. (This was true in Windows 3.1 though apparently not any more.) The handle doesn’t “become” cross-process any more than a window handle “becomes” cross-process. The DC cache is per-thread (or at least was in Windows 3.1; things may be different nowadays). If a thread calls “GetDC”, it looks in the cache of DCs recently released by that thread. The two concepts (visibility and cache affinity) are unrelated. Passports are per-country but are globally valid – nobody seems confused by that. -Raymond]

Yeah, well, formal documentation is no substitute for you either.
I think in the thread that started one day after this one,
someone guessed that your reason for posting this series is that you’re
not authorized to fix the formal documentation.

[This type of article is not suitable for
formal documentation. For one thing, it’s too informal. For another
thing, formal documentation is about explaining what each flag does and
letting you decide which ones you want. If you want to use CS_CLASSDC
then more power to you. Formal documentation is not going to say
“CS_CLASSDC is hard to use correctly, so be careful.” -Raymond]

> By “cross-process” I meant that a DC handle,

> once created, is valid in any process [in

> Windows 3.1]. […] If a thread calls

> “GetDC”, it looks in the cache of DCs

> recently released by that thread.

So in 3.1, if this thread’s cache was empty and GetDC had to take a
DC that isn’t from this thread’s cache, it still could have obtained a
corrupted DC that a different thread released?

[What part of “per-thread” don’t you
understand? If one thread could look in another thread’s cache, then
it’s not a per-thread cache, now, is it? I can’t tell if you’re
genuinely confused or are being intentionally obtuse to prove some
point. -Raymond]

Meanwhile…

> Passports are per-country but are globally

> valid

Wanna bet?

[I should have know that you of all people would pointlessly nitpick this analogy. -Raymond]

So the way I understand what you’re saying, thread 2 always gets a
different DC than thread 1; thread 2’s DC will never be dirty (have the
wrong object(s) selected into it) unless thread 2 left it that way
itself.

[Or unless you’re using CS_OWNDC or CS_CLASSDC, which was the point of this mini-series. -Raymond]

Not that this excuses any practice of releasing dirty DCs, but at least you won’t be breaking anyone else’s window.

Actually, re-reading the comments here, it seems you said that at
least once before, way back up under Reinder’s comment June 1.
Hmm.

I *think* Norman meant a scenario like this (if not, I’m wondering what happens):

Thread 1 calls GetDC, selects some object into the returned DC, and
releases it. The DC gets put back into thread 1’s cache, in a
“dirty” state.

Then, thread 2 gets a DC for the same window (…is this even
legal, since the window is owned by thread 1?). No DC for that
window exists in thread 2’s cache. Does it get the dirty DC that
thread 1 released, or does the window manager create a new DC? If
thread 2 gets the dirty DC, does it get a clean one after the dirty one
ages out of thread 1’s cache?

[Thread 2 can’t see thread 1’s cache. There could be a
kangaroo in thread 1’s cache, it won’t have any effect on thread 2.
Note that the fact that the cache is per-thread is an implementation
detail and can change at any time. In fact, it may have already changed
and I simply haven’t noticed. -Raymond]

>> By “cross-process” I meant that a DC >>> handle, once created, is valid in any >>> process [in Windows 3.1]. […] If a >>> thread calls “GetDC”, it looks in the >>> cache of DCs recently released by that >>> thread. >> >> So in 3.1, if this thread’s cache was empty >> and GetDC had to take a DC that isn’t from >> this thread’s cache, it still could have >> obtained a corrupted DC that a different >> thread released? > > [What part of “per-thread” don’t you > understand?

Maybe the part that turns a cache into an infinite supply? You said there’s a per-thread cache and that a released DC goes into the thread’s cache. Well the same thread is capable of getting DCs again. After it’s gotten all the DCs that it previously released, its cache is empty, right? When it tries to get another DC, can it get one?

When a thread exits, do its corrupted cached DCs get leaked or do they rejoin a pool of available DCs?

If all currently unused DCs are on the caches of other threads, and our thread tries to get another DC, does Windows inherit a famous old DEC error message? (“Memory available, but not for you.”) Or does Windows take a DC in order to get it usable where needed?

And still, still, if DCs are cross-process then Reinder’s question still looks mighty valid.

[This article was not about the details of the DC cache. The cache has a policies to cover these cases and they are pretty much in line with most other cache policies And I already answered Reinder’s question: The cache is per thread. All you’re corrupting is yourself. -Raymond]

I agree with Miral. In fact some pages in MSDN say that certain items are defined only for backwards compatibility with 16-bit Windows and new applications should not use them. I think such notices have even been added to more pages recently, belatedly but better than never.