{"id":31003,"date":"2006-06-01T10:00:06","date_gmt":"2006-06-01T10:00:06","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2006\/06\/01\/what-does-the-cs_owndc-class-style-do\/"},"modified":"2006-06-01T10:00:06","modified_gmt":"2006-06-01T10:00:06","slug":"what-does-the-cs_owndc-class-style-do","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20060601-06\/?p=31003","title":{"rendered":"What does the CS_OWNDC class style do?"},"content":{"rendered":"<p>\nRecall that window DCs are most commonly used only temporarily.\nIf you need to draw into a window, you call\n<code>BeginPaint<\/code>\nor, if outside of a paint cycle, <code>GetDC<\/code>,\nalthough painting outside of a paint cycle is generally to be avoided.\nThe window manager produces a DC for the window and returns it.\nYou use the DC, then restore it to its original state and return\nit to the window manager with\n<code>EndPaint<\/code> (or <code>ReleaseDC<\/code>).\nInternally, the window manager keeps a small cache of DCs which it\ndips into when people come asking for a window DC,\nand when the DC is returned, it goes back into the cache.\nSince window DCs are used only temporarily, the number of outstanding DCs\nis typically not more than a handful, and a small cache is sufficient\nto satisfy DC demands in a normally-running system.\n<\/p>\n<p>\nIf you register a window class and include the <code>CS_OWNDC<\/code>\nflag in the class styles,\nthen the window manager creates a DC for the window and puts it into\nthe DC cache with a special tag that means\n&#8220;Do not purge this DC from the DC cache\nbecause it&#8217;s the <code>CS_OWNDC<\/code> for this window.&#8221;\nIf you call <code>BeginPaint<\/code> or <code>GetDC<\/code> to get\na DC for a <code>CS_OWNDC<\/code> window,\nthen that DC will always be found and returned\n(since it was marked as &#8220;never purge&#8221;).\nThe consequences of this are good, bad, and worse.\n<\/p>\n<p>\nThe good part is that since the DC has been created specially for\nthe window and is never purged,\nyou don&#8217;t have to worry about &#8220;cleaning up the DC&#8221; before\nreturning it to the cache.\nWhenever you call <code>BeginPaint<\/code> or <code>GetDC<\/code>\nfor a <code>CS_OWNDC<\/code> window,\nyou always get that special DC back.\nIndeed, that&#8217;s the whole point of <code>CS_OWNDC<\/code> windows:\nYou can create a <code>CS_OWNDC<\/code> window,\nget its DC, set it up the way you like it\n(selecting fonts, setting colors, <i>etc<\/i>.), and even if you\nrelease the DC and get it again later, you will get that\nsame DC back and it will be just the way you left it.\n<\/p>\n<p>\nThe bad part is that you&#8217;re taking something that was meant to be\nused only temporarily (a window DC) and using it permanently.\nEarly versions of Windows had a very low limit for DCs (eight or so),\nso it was crucial that DCs be released as soon as they weren&#8217;t needed.\nThat limit has since been raised significantly, but the underlying\nprinciple remains: DCs should not be allocated carelessly.\nYou may have noticed that the implementation of <code>CS_OWNDC<\/code>\nstill uses the DC cache; it&#8217;s just that those DCs get a special marking\nso the DC manager knows to treat them specially.\nThis means that a large number of <code>CS_OWNDC<\/code> DCs end up\n&#8220;polluting&#8221; the DC cache, slowing down future calls to\nfunctions like <code>BeginPaint<\/code> and <code>ReleaseDC<\/code>\nthat need to search through the DC cache.\n<\/p>\n<p>\n(Why wasn&#8217;t the DC manager optimized to handle the case of\na large number of <code>CS_OWNDC<\/code> DCs?\nFirst, as I already noted, the original DC manager didn&#8217;t have\nto worry about the case of a large number of DCs since the system\nsimply couldn&#8217;t even create that many in the first place.\nSecond, even after the limit on the number of DCs was raised,\nthere wasn&#8217;t much point in rewriting the DC manager to optimize\nthe handling of <code>CS_OWNDC<\/code> DCs since programmers were\nalready told to use <code>CS_OWNDC<\/code> sparingly.\nThis is one of the practicalities of software engineering:\nYou can do only so much.\nEverything you decide to do comes at the expense of something else.\nIt&#8217;s hard to justify optimizing a scenario that programmers were told\nto avoid and which they in fact were already avoiding.\nYou don&#8217;t optimize for the case where somebody is abusing your system.\nIt&#8217;s like spending time designing a car&#8217;s engine so it maintained\ngood gas mileage when the car has no oil.)\n<\/p>\n<p>\nThe worse part is that most windowing framework libraries and\nnearly all sample code\nassume that your windows are not <code>CS_OWNDC<\/code> windows.\nConsider the following code that draws text in two fonts,\nusing the first font to guide the placement of characters in the second.\nIt looks perfectly fine, doesn&#8217;t it?\n<\/p>\n<pre>\nvoid FunnyDraw(HWND hwnd, HFONT hf1, HFONT hf2)\n{\n HDC hdc1 = GetDC(hwnd);\n HFONT hfPrev1 = SelectFont(hdc1, hf1);\n UINT taPrev1 = SetTextAlign(hdc1, TA_UPDATECP);\n MoveToEx(hdc1, 0, 0, NULL);\n HDC hdc2 = GetDC(hwnd);\n HFONT hfPrev2 = SelectFont(hdc2, hf2);\n for (LPTSTR psz = TEXT(\"Hello\"); *psz; psz++) {\n  POINT pt;\n  GetCurrentPositionEx(hdc1, &amp;pt);\n  TextOut(hdc2, pt.x, pt.y + 30, psz, 1);\n  TextOut(hdc1, 0, 0, psz, 1);\n }\n SelectFont(hdc1, hfPrev1);\n SelectFont(hdc2, hfPrev2);\n SetTextAlign(hdc1, taPrev1);\n ReleaseDC(hwnd, hdc1);\n ReleaseDC(hwnd, hdc2);\n}\n<\/pre>\n<p>\nWe get two DCs for the window.\nIn the first we select our first font; in the second, we select the second.\nIn the first DC, we also set the text alignment to <code>TA_UPDATECP<\/code>\nwhich means that the coordinates passed to the <code>TextOut<\/code>\nfunction will be ignored.\nInstead the text will be drawn starting at the &#8220;current position&#8221;\nand the &#8220;current position&#8221; will be updated to the end of the string,\nso that the next call to <code>TextOut<\/code> will resume where the\nprevious one left off.\n<\/p>\n<p>\nOnce the two DCs are set up, we draw our string one character at a time.\nWe query the first DC for the current position and draw the character\nin the second font at that same <i>x<\/i>-coordinate (but a bit lower),\nthen we draw the character in the first font (which also advances the\ncurrent position).\n<\/p>\n<p>\nAfter the text drawing loop is done, we restore the states of the two\nDCs as part of the standard bookkeeping.\n<\/p>\n<p>\nThe intent of the function is to draw something like this,\nwhere the first font is bigger than the second.\n<\/p>\n<table CELLSPACING=\"0\" CELLPADDING=\"0\">\n<tr STYLE=\"font-size: 200%\">\n<td>H<\/td>\n<td>e<\/td>\n<td>l<\/td>\n<td>l<\/td>\n<td>o<\/td>\n<\/tr>\n<tr>\n<td>H<\/td>\n<td>e<\/td>\n<td>l<\/td>\n<td>l<\/td>\n<td>o<\/td>\n<\/tr>\n<\/table>\n<p>\nAnd if the window is not <code>CS_OWNDC<\/code> that&#8217;s what you get.\nYou can try it out by calling it from our scratch program:\n<\/p>\n<pre>\nHFONT g_hfBig;\nBOOL\nOnCreate(HWND hwnd, LPCREATESTRUCT lpcs)\n{\n LOGFONT lf;\n GetObject(GetStockFont(ANSI_VAR_FONT),\n           sizeof(lf), &amp;lf);\n lf.lfHeight *= 2;\n g_hfBig = CreateFontIndirect(&amp;lf);\n return g_hfBig != NULL;\n}\nvoid\nOnDestroy(HWND hwnd)\n{\n if (g_hfBig) DeleteObject(g_hfBig);\n PostQuitMessage(0);\n}\nvoid\nPaintContent(HWND hwnd, PAINTSTRUCT *pps)\n{\n FunnyDraw(hwnd, g_hfBig,\n                 GetStockFont(ANSI_VAR_FONT));\n}\n<\/pre>\n<p>\nBut if the window is <code>CS_OWNDC<\/code>, then bad things happen.\nTry it yourself by changing the line\n<code>wc.style = 0;<\/code>\nto\n<code>wc.style = CS_OWNDC;<\/code>\nYou get the following unexpected output:\n<\/p>\n<table CELLSPACING=\"0\" CELLPADDING=\"0\">\n<tr>\n<td>HHeelllloo<\/td>\n<\/tr>\n<\/table>\n<p>\nOf course, if you understand how <code>CS_OWNDC<\/code> works,\nthis is hardly unexpected at all.\nThe key to understanding is remembering that when the window\nis <code>CS_OWNDC<\/code> then <code>GetDC<\/code> just returns the\nsame DC back no matter how many times you call it.\nNow all you have to do is walk through the\n<code>FunnyDraw<\/code> function remembering that\n<code>hdc1<\/code> and <code>hdc2<\/code> are in fact\n<strong>the same thing<\/strong>.\n<\/p>\n<pre>\nvoid FunnyDraw(HWND hwnd, HFONT hf1, HFONT hf2)\n{\n HDC hdc1 = GetDC(hwnd);\n HFONT hfPrev1 = SelectFont(hdc1, hf1);\n UINT taPrev1 = SetTextAlign(hdc1, TA_UPDATECP);\n MoveToEx(hdc1, 0, 0, NULL);\n<\/pre>\n<p>\nSo far, execution of the function is pretty normal.\n<\/p>\n<pre>\n HDC hdc2 = GetDC(hwnd);\n<\/pre>\n<p>\nSince the window is a <code>CS_OWNDC<\/code> window,\nthe DC that is returned in <code>hdc2<\/code> is the same\none that was returned in <code>hdc1<\/code>.\nIn other words, <code>hdc1 == hdc2<\/code>!\nNow things get exciting.\n<\/p>\n<pre>\n HFONT hfPrev2 = SelectFont(hdc2, hf2);\n<\/pre>\n<p>\nSince <code>hdc1 == hdc2<\/code>,\nwhat this really does is deselect the font <code>hf1<\/code>\nfrom the DC and select the font <code>hf2<\/code> instead.\n<\/p>\n<pre>\n for (LPTSTR psz = TEXT(\"Hello\"); *psz; psz++) {\n  POINT pt;\n  GetCurrentPositionEx(hdc1, &amp;pt);\n  TextOut(hdc2, pt.x, pt.y + 30, psz, 1);\n  TextOut(hdc1, 0, 0, psz, 1);\n }\n<\/pre>\n<p>\nNow this loop completely falls apart.\nAt the first iteration, we retrieve the current position from\nthe DC, which returns (0,&nbsp;0) since we haven&#8217;t moved it yet.\nWe then draw the letter &#8220;H&#8221; at position (0,&nbsp;30) into the\nsecond DC.\nBut since the second DC is the same as the first one, what\nreally happens is that we are calling <code>TextOut<\/code> into\na DC that is in <code>TA_UPDATECP<\/code> mode.\nThus, the coordinates are ignored, the letter &#8220;H&#8221; is displayed\n(in the second font), and the current position is updated to\nbe after the &#8220;H&#8221;.\nFinally, we draw the &#8220;H&#8221; into the first DC (which is the same as\nthe second).\nWe think we&#8217;re drawing it with the first font, but in fact\nwe&#8217;re drawing with the second font.\nWe think we&#8217;re drawing at (0,&nbsp;0), but in fact we&#8217;re\ndrawing at (<i>x<\/i>,&nbsp;0), where <i>x<\/i> is the width of\nthe letter &#8220;H&#8221;,\nbecause the call to <code>TextOut(hdc2,&nbsp;...)<\/code>\nupdated the current position.\n<\/p>\n<p>\nThus, each time through the loop, the next character in the\nstring is displayed twice, all in the second font.\n<\/p>\n<p>\nBut wait, the disaster isn&#8217;t finished yet.\nLook at our cleanup code:\n<\/p>\n<pre>\n SelectFont(hdc1, hfPrev1);\n<\/pre>\n<p>\nThis restores the original font into the DC.\n<\/p>\n<pre>\n SelectFont(hdc2, hfPrev2);\n<\/pre>\n<p>\nThis re-selects the first font!\nWe failed to restore the DC to its original state\nand ended up putting a &#8220;corrupted&#8221; DC into the cache.\n<\/p>\n<p>\nThat&#8217;s why I described <code>CS_OWNDC<\/code> as &#8220;worse&#8221;.\nIt takes code that used to work and breaks it\nby violating assumptions that most people make (usually\nwithout realizing it) about DCs.\n<\/p>\n<p>\nAnd you thought <code>CS_OWNDC<\/code> was bad.\nNext time I&#8217;ll talk about the disaster that is known as\n<code>CS_CLASSDC<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25,2],"class_list":["post-31003","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code","tag-history"],"acf":[],"blog_post_summary":"<p>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 [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/31003","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=31003"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/31003\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=31003"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=31003"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=31003"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}