{"id":4023,"date":"2013-06-21T07:00:00","date_gmt":"2013-06-21T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/06\/21\/of-what-use-is-the-rdw_internalpaint-flag\/"},"modified":"2013-06-21T07:00:00","modified_gmt":"2013-06-21T07:00:00","slug":"of-what-use-is-the-rdw_internalpaint-flag","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130621-00\/?p=4023","title":{"rendered":"Of what use is the RDW_INTERNALPAINT flag?"},"content":{"rendered":"<p><P>\nFor motivational purposes, let&#8217;s start with\n<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/05\/13\/10417964.aspx\">\na program that displays a DWM thumbnail<\/A>.\n<\/P>\n<P>\nStart with\n<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2003\/07\/23\/54576.aspx\">\nthe scratch program<\/A>\nand add the following:\n<\/P>\n<PRE>\n<FONT COLOR=\"blue\">#include &lt;dwmapi.h&gt;<\/p>\n<p>HWND g_hwndThumbnail;\nHTHUMBNAIL g_hthumb;<\/p>\n<p>void UpdateThumbnail(HWND hwndFrame, HWND hwndTarget)\n{\n if (g_hwndThumbnail != hwndTarget) {\n  g_hwndThumbnail = hwndTarget;\n  if (g_hthumb != nullptr) {\n   DwmUnregisterThumbnail(g_hthumb);\n   g_hthumb = nullptr;\n  }<\/p>\n<p>  if (hwndTarget != nullptr) {\n   RECT rcClient;\n   GetClientRect(hwndFrame, &amp;rcClient);\n   if (SUCCEEDED(DwmRegisterThumbnail(hwndFrame,\n                         g_hwndThumbnail, &amp;g_hthumb))) {\n    DWM_THUMBNAIL_PROPERTIES props = {};\n    props.dwFlags = DWM_TNP_RECTDESTINATION | DWM_TNP_VISIBLE;\n    props.rcDestination = rcClient;\n    props.rcDestination.top += 50;\n    props.fVisible = TRUE;\n    DwmUpdateThumbnailProperties(g_hthumb, &amp;props);\n   }\n  }\n }\n}<\/FONT>\n<\/PRE>\n<P>\nThe <CODE>Update&shy;Thumbnail<\/CODE>\nfunction positions a thumbnail of the target window\ninside the frame window.\nThere&#8217;s a small optimization in the case that the\ntarget window is the same one that the thumbnail\nis already viewing.\nOverall, no big deal.\n<\/P>\n<PRE>\nvoid\nOnDestroy(HWND hwnd)\n{\n <FONT COLOR=\"blue\">UpdateThumbnail(hwnd, nullptr);<\/FONT>\n PostQuitMessage(0);\n}\n<\/PRE>\n<P>\nWhen our window is destroyed, we need to clean up the thumbnail,\nwhich we do by updating it to a null pointer.\n<\/P>\n<P>\nFor the purpose of illustration,\nlet&#8217;s say that pressing the <KBD>1<\/KBD> key changes\nthe thumbnail to a randomly-selected window.\n<\/P>\n<PRE>\n<FONT COLOR=\"blue\">struct RANDOMWINDOWINFO\n{\n HWND hwnd;\n int cWindows;\n};<\/p>\n<p>BOOL CALLBACK RandomEnumProc(HWND hwnd, LPARAM lParam)\n{\n if (hwnd != g_hwndThumbnail &amp;&amp;\n     IsWindowVisible(hwnd) &amp;&amp;\n     (GetWindowStyle(hwnd) &amp; WS_CAPTION) == WS_CAPTION) {\n  auto prwi = reinterpret_cast&lt;RANDOMWINDOWINFO *&gt;(lParam);\n  prwi-&gt;cWindows++;\n  if (rand() % prwi-&gt;cWindows == 0) {\n   prwi-&gt;hwnd = hwnd;\n  }\n }\n return TRUE;\n}<\/p>\n<p>void ChooseRandomWindow(HWND hwndFrame)\n{\n RANDOMWINDOWINFO rwi = {};\n EnumWindows(RandomEnumProc, reinterpret_cast&lt;LPARAM&gt;(&amp;rwi));\n UpdateThumbnail(hwndFrame, rwi.hwnd);\n}<\/p>\n<p>void OnChar(HWND hwnd, TCHAR ch, int cRepeat)\n{\n switch (ch) {\n case TEXT(&#8216;1&#8217;):\n  ChooseRandomWindow(hwnd);\n  break;\n }\n}<\/p>\n<p> HANDLE_MESSAGE(hwnd, WM_CHAR, OnChar);<\/FONT>\n<\/PRE>\n<P>\nThe random window selector selects among windows with\na caption which are visible and which are not already\nbeing shown in the thumbnail.\n(That last bit is so that when you press <KBD>1<\/KBD>,\nit will always pick a <I>different<\/I> window.)\n<\/P>\n<P>\nRun this program, and yippee, whenever you press the\n<KBD>1<\/KBD> key, you get a new thumbnail.\n<\/P>\n<P>\nOkay, but usually your program shows more than just a thumbnail.\nIt probably incorporates the thumbnail into its other content,\nso let&#8217;s draw some other content, too.\nSay, a single-character random message.\n<\/P>\n<PRE>\n<FONT COLOR=\"blue\">TCHAR g_chMessage = &#8216;?&#8217;;<\/FONT><\/p>\n<p>void\nPaintContent(HWND hwnd, PAINTSTRUCT *pps)\n{\n <FONT COLOR=\"blue\">if (!<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2006\/03\/27\/561924.aspx\">IsRectEmpty<\/A>(&amp;pps-&gt;rcPaint)) {\n  RECT rcClient;\n  GetClientRect(hwnd, &amp;rcClient);\n  DrawText(pps-&gt;hdc, &amp;g_chMessage, 1, &amp;rcClient,\n           DT_TOP | DT_CENTER);\n }<\/FONT>\n}<\/p>\n<p><FONT COLOR=\"blue\">void ChooseRandomMessage(HWND hwndFrame)\n{\n g_chMessage = rand() % 26 + TEXT(&#8216;A&#8217;);\n InvalidateRect(hwndFrame, nullptr, TRUE);\n}<\/FONT><\/p>\n<p>void OnChar(HWND hwnd, TCHAR ch, int cRepeat)\n{\n switch (ch) {\n case TEXT(&#8216;1&#8217;):\n  ChooseRandomWindow(hwnd);\n  break;\n <FONT COLOR=\"blue\">case TEXT(&#8216;2&#8217;):\n  ChooseRandomMessage(hwnd);\n  break;<\/FONT>\n }\n}\n<\/PRE>\n<P>\nNow, if you press <KBD>2<\/KBD>,\nwe change the random message.\nThere is a small optimiztion in\n<CODE>Paint&shy;Content<\/CODE> that skips the rendering\nif the paint rectangle is empty.\nAgain, no big deal.\n<\/P>\n<P>\nOkay, but sometimes there are times where your program\nwants to update the thumbnail <I>and<\/I> the message\nat the same time.\nLike this:\n<\/P>\n<PRE>\nvoid OnChar(HWND hwnd, TCHAR ch, int cRepeat)\n{\n switch (ch) {\n case TEXT(&#8216;1&#8217;):\n  ChooseRandomWindow(hwnd);\n  break;\n case TEXT(&#8216;2&#8217;):\n  ChooseRandomMessage(hwnd);\n  break;\n <FONT COLOR=\"blue\">case TEXT(&#8216;3&#8217;):\n  ChooseRandomWindow(hwnd);\n  ChooseRandomMessage(hwnd);\n  break;<\/FONT>\n }\n}\n<\/PRE>\n<P>\nRun this program and press <KBD>3<\/KBD>\nand watch the thumbnail and message change simultaneously.\n<\/P>\n<P>\nAnd now we have a problem.\n<\/P>\n<P>\nYou see, the\n<CODE>Choose&shy;Random&shy;Window<\/CODE> function updates\nthe thumbnail immediately,\nsince the thumbnail is presented by DWM,\nwhereas the\n<CODE>Choose&shy;Random&shy;Message<\/CODE> function updates\nthe message, but the new message doesn&#8217;t appear on the screen\nuntil the next paint cycle.\nThis means that there is a window of time where the new\nthumbnail is on the screen, but you still have the old message.\nSince painting is a low-priority activity,\nthe window manager is going to deliver other messages to your\nwindow before it finally gets around to painting,\nand the visual mismatch between the thumbnail and the message can\nlast for quite some time.\n(You can exaggerate this in the sample program by inserting a\ncall to <CODE>Sleep<\/CODE>.)\nWhat can we do to get rid of this visual glitch?\n<\/P>\n<P>\nOne solution would be to delay updating the thumbnail\nuntil the next paint cycle.\nAt the paint cycle, we update the thumbnail <I>and<\/I>\nrender the new message.\nNow both updates occur at the same time,\nand you get rid of the glitch.\nTo trigger a paint cycle, we can invalidate\na dummy 1&times;1 pixel in the window.\n<\/P>\n<PRE>\n<FONT COLOR=\"blue\">HWND g_hwndThumbnailWanted;<\/FONT><\/p>\n<p>void\nPaintContent(HWND hwnd, PAINTSTRUCT *pps)\n{\n <FONT COLOR=\"blue\">UpdateThumbnail(hwnd, g_hwndThumbnailWanted);<\/FONT><\/p>\n<p> if (!IsRectEmpty(&amp;pps-&gt;rcPaint)) {\n  RECT rcClient;\n  GetClientRect(hwnd, &amp;rcClient);\n  DrawText(pps-&gt;hdc, &amp;g_chMessage, 1, &amp;rcClient,\n           DT_TOP | DT_CENTER);\n }\n}<\/p>\n<p>void ChooseRandomWindow(HWND hwndFrame)\n{\n RANDOMWINDOWINFO rwi = {};\n EnumWindows(RandomEnumProc, reinterpret_cast(&amp;rwi));\n <FONT COLOR=\"blue\">g_hwndThumbnailWanted = rwi.hwnd;\n RECT rcDummy = { 0, 0, 1, 1 };\n InvalidateRect(hwndFrame, &amp;rcDummy, FALSE);<\/FONT>\n}\n<\/PRE>\n<P>\nNow, when we want to change the thumbnail, we just\nremember what thumbnail we want (the &#8220;logical&#8221; current\nthumbnail)\nand invalidate a dummy pixel in our window.\nThe invalid dummy pixel triggers a paint cycle,\nand in our paint cycle, we call\n<CODE>Update&shy;Thumbnail<\/CODE> to synchronize\nthe logical current thumbnail with the physical\ncurrent thumbnail.\nAnd then we continue with our regular painting\n(in case there is also painting to be done, too).\n<\/P>\n<P>\nBut it sure feels wasteful invalidating a pixel\nand forcing the <CODE>Draw&shy;Text<\/CODE> to occur\neven though we really didn&#8217;t update anything.\nWouldn&#8217;t it be great if we could just say,\n&#8220;Could you fire up a paint cycle for me,\neven though there&#8217;s technically nothing to paint?\nBecause I actually do have stuff to paint,\nit&#8217;s just something outside your knowledge\nsince it is not rendered by GDI.&#8221;\n<\/P>\n<P>\nEnter the\n<CODE>RDW_INTERNAL&shy;PAINT<\/CODE> flag.\n<\/P>\n<P>\nIf you pass the\n<CODE>RDW_INTERNAL&shy;PAINT<\/CODE> flag\nto <CODE>Redraw&shy;Window<\/CODE>,\nthat means,\n&#8220;Set the &#8216;Yo, there&#8217;s painting to be done!&#8217; flag.\nI know you think there&#8217;s no actual painting to be done,\nbut trust me on this.&#8221;\n(<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/12\/19\/10249000.aspx\">It&#8217;s\nnot actually a flag, but you can think of it that way<\/A>.)\n<\/P>\n<P>\nWhen the window manager then get around to deciding whether\nthere is any painting to be done,\nbefore it concludes,\n&#8220;Nope, this window is all valid,&#8221;\nit checks if you made a special\n<CODE>RDW_INTERNAL&shy;PAINT<\/CODE> request,\nand if so, then it will generate\na dummy <CODE>WM_PAINT<\/CODE> message for you.\n<\/P>\n<P>\nUsing this new flag is simple:\n<\/P>\n<PRE>\n g_hwndThumbnailWanted = rwi.hwnd;\n <FONT COLOR=\"red\">\/\/ <STRIKE>RECT rcDummy = { 0, 0, 1, 1 };<\/STRIKE>\n \/\/ <STRIKE>InvalidateRect(hwndFrame, &amp;rcDummy, FALSE);<\/STRIKE><\/FONT>\n <FONT COLOR=\"blue\">RedrawWindow(hwndFrame, nullptr, nullptr,\n              RDW_INTERNALPAINT);<\/FONT>\n<\/PRE>\n<P>\nNow, when the program wants to update its thumbnail,\nit just schedules a fake-paint message with the window manager.\nThese fake-paint messages coalesce with real-paint messages,\nso if you do an internal paint and an invalidation,\nonly one actual paint message will be generated.\nIf the paint message is a fake-paint message,\nthe <CODE>rcPaint<\/CODE> will be empty,\nand you can test for that in your\npaint handler and skip your GDI painting.\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>For motivational purposes, let&#8217;s start with a program that displays a DWM thumbnail. Start with the scratch program and add the following: #include &lt;dwmapi.h&gt; HWND g_hwndThumbnail; HTHUMBNAIL g_hthumb; void UpdateThumbnail(HWND hwndFrame, HWND hwndTarget) { if (g_hwndThumbnail != hwndTarget) { g_hwndThumbnail = hwndTarget; if (g_hthumb != nullptr) { DwmUnregisterThumbnail(g_hthumb); g_hthumb = nullptr; } if (hwndTarget != [&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],"class_list":["post-4023","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>For motivational purposes, let&#8217;s start with a program that displays a DWM thumbnail. Start with the scratch program and add the following: #include &lt;dwmapi.h&gt; HWND g_hwndThumbnail; HTHUMBNAIL g_hthumb; void UpdateThumbnail(HWND hwndFrame, HWND hwndTarget) { if (g_hwndThumbnail != hwndTarget) { g_hwndThumbnail = hwndTarget; if (g_hthumb != nullptr) { DwmUnregisterThumbnail(g_hthumb); g_hthumb = nullptr; } if (hwndTarget != [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4023","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=4023"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4023\/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=4023"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=4023"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=4023"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}