{"id":32793,"date":"2006-01-03T03:20:12","date_gmt":"2006-01-03T03:20:12","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2006\/01\/03\/taxes-remote-desktop-connection-and-painting\/"},"modified":"2006-01-03T03:20:12","modified_gmt":"2006-01-03T03:20:12","slug":"taxes-remote-desktop-connection-and-painting","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20060103-12\/?p=32793","title":{"rendered":"Taxes: Remote Desktop Connection and painting"},"content":{"rendered":"<p>\nAn increasingly important developer tax is supporting\nRemote Desktop Connection properly.\nWhen the user is connected via a Remote Desktop Connection,\nvideo operations are transferred over the network connection\nto the client for display.\nSince networks have high latency and nowhere near the bandwidth\nof a local PCI or AGP bus,\nyou need to adapt to the changing cost of drawing to the screen.\n<\/p>\n<p>\nIf you draw a line on the screen, the &#8220;draw line&#8221; command is\nsent over the network to the client.\nIf you draw text, a &#8220;draw text&#8221; command is sent (along with the\ntext to draw).\nSo far so good.\nBut if you copy a bitmap to the screen, the entire\nbitmap needs to be transferred over the network.\n<\/p>\n<p>\nLet&#8217;s write a sample program that illustrates this point.\nStart with our\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2005\/04\/22\/410773.aspx\">\nnew scratch program<\/a>\nand make the following changes:\n<\/p>\n<pre>\nvoid Window::Register()\n{\n    WNDCLASS wc;\n    wc.style         = <font COLOR=\"blue\">CS_VREDRAW | CS_HREDRAW<\/font>;\n    wc.lpfnWndProc   = Window::s_WndProc;\n    ...\n}\nclass RootWindow : public Window\n{\npublic:\n virtual LPCTSTR ClassName() { return TEXT(\"Scratch\"); }\n static RootWindow *Create();\nprotected:\n LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);\n LRESULT OnCreate();\n <font COLOR=\"blue\">void PaintContent(PAINTSTRUCT *pps);\n void Draw(HDC hdc, PAINTSTRUCT *pps);<\/font>\nprivate:\n HWND m_hwndChild;\n};\n<font COLOR=\"blue\">void RootWindow::Draw(HDC hdc, PAINTSTRUCT *pps)\n{\n FillRect(hdc, &amp;pps-&gt;rcPaint, (HBRUSH)(COLOR_WINDOW + 1));\n RECT rc;\n GetClientRect(m_hwnd, &amp;rc);\n for (int i = -10; i &lt; 10; i++) {\n  TextOut(hdc, 0, i * 15 + rc.bottom \/ 2, TEXT(&quot;Blah blah&quot;), 9);\n }\n}\nvoid RootWindow::PaintContent(PAINTSTRUCT *pps)\n{\n Draw(pps-&gt;hdc, pps);\n}<\/font>\nLRESULT RootWindow::HandleMessage(\n                          UINT uMsg, WPARAM wParam, LPARAM lParam)\n{\n switch (uMsg) {\n ...\n <font COLOR=\"blue\">case WM_ERASEBKGND: return 1;<\/font>\n ...\n}\n<\/pre>\n<p>\nThere is an odd division of labor here;\nthe <code>PaintContent<\/code> method doesn&#8217;t actually do\nanything aside from handing the work off to the <code>Draw<\/code>\nmethod to do the actual drawing.\n(You&#8217;ll see why soon.)\nMake sure &#8220;Show window contents while dragging&#8221; is enabled and\nrun this program and resize it vertically.\nUgh, what ugly flicker.\nWe fix this by the traditional technique of double-buffering.\n<\/p>\n<pre>\nvoid RootWindow::PaintContent(PAINTSTRUCT *pps)\n{\n <font COLOR=\"blue\">if (!IsRectEmpty(&amp;pps-&gt;rcPaint)) {\n  HDC hdc = CreateCompatibleDC(pps-&gt;hdc);\n  if (hdc) {\n   int x = pps-&gt;rcPaint.left;\n   int y = pps-&gt;rcPaint.top;\n   int cx = pps-&gt;rcPaint.right - pps-&gt;rcPaint.left;\n   int cy = pps-&gt;rcPaint.bottom - pps-&gt;rcPaint.top;\n   HBITMAP hbm = CreateCompatibleBitmap(pps-&gt;hdc, cx, cy);\n   if (hbm) {\n    HBITMAP hbmPrev = SelectBitmap(hdc, hbm);\n    SetWindowOrgEx(hdc, x, y, NULL);\n    Draw(hdc, pps);\n    BitBlt(pps-&gt;hdc, x, y, cx, cy, hdc, x, y, SRCCOPY);\n    SelectObject(hdc, hbmPrev);\n    DeleteObject(hbm);\n   }\n   DeleteDC(hdc);\n  }\n }<\/font>\n}\n<\/pre>\n<p>\nOur new <code>PaintContent<\/code> method creates an offscreen bitmap\nand asks the <code>Draw<\/code> method to draw into it.\nOnce that&#8217;s done, the results are copied to the screen at one go,\nthereby avoiding flicker.\nIf you run this program, you&#8217;ll see that it resizes nice and smooth.\n<\/p>\n<p>\nNow connect to the computer via a Remote Desktop Connection\nand run it again.\nSince Remote Desktop Connection disables &#8220;Show window contents\nwhile dragging&#8221;, you can&#8217;t use resizing to trigger redraws,\nso instead maximize the program and restore it a few times.\nNotice the long delay before the window is resized when you\nmaximize it.\nThat&#8217;s because we are pumping a huge bitmap across the\nRemote Desktop Connection as part of that <code>BitBlt<\/code>\ncall.\n<\/p>\n<p>\nGo back to the old version of the <code>PaintContent<\/code>\nmethod, the one that just calls <code>Draw<\/code>,\nand run it over Remote Desktop Connection.\nAh, this one is fast.\nThat&#8217;s because the simpler version doesn&#8217;t transfer a huge\nbitmap over the Remote Desktop Connection;\nit just sends twenty <code>TextOut<\/code> calls on a pretty\nshort string of text.\nThese take up much less bandwidth than a 1024&#215;768 bitmap.\n<\/p>\n<p>\nWe have one method that is faster over a Remote Desktop Connection,\nand another method that is faster when run locally.\nWhich should we use?\n<\/p>\n<p>\nWe use both,\nchoosing our drawing method based on whether the program\nis running over a Remote Desktop Connection.\n<\/p>\n<pre>\nvoid RootWindow::PaintContent(PAINTSTRUCT *pps)\n{\n <font COLOR=\"blue\">if (GetSystemMetrics(SM_REMOTESESSION)) {\n  Draw(pps-&gt;hdc, pps);\n } else<\/font> if (!IsRectEmpty(&amp;pps-&gt;rcPaint)) {\n  ... as before ...\n }\n}\n<\/pre>\n<p>\nNow we get the best of both worlds.\nWhen run locally, we use the double-buffered drawing which\ndraws without flickering,\nbut when run over a Remote Desktop Connection, we use the simple\n<code>Draw<\/code> method that draws directly to the screen\nrather than to an offscreen bitmap.\n<\/p>\n<p>\nThis is a rather simple example of adapting to Remote Desktop\nConnection.\nIn a more complex world, you may have more complicated data\nstructures associated with the two styles of drawing,\nor you may have background activities related to drawing that\nyou may want to turn on and off based on whether the program\nis running over a Remote Desktop Connection.\nSince the user can dynamically connect and disconnect,\nyou can&#8217;t just assume that the state of the Remote Desktop\nConnection when your program starts\nwill be the state for the lifetime of the program.\nWe&#8217;ll see next time how we can adapt to a changing world.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>An increasingly important developer tax is supporting Remote Desktop Connection properly. When the user is connected via a Remote Desktop Connection, video operations are transferred over the network connection to the client for display. Since networks have high latency and nowhere near the bandwidth of a local PCI or AGP bus, you need to adapt [&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-32793","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>An increasingly important developer tax is supporting Remote Desktop Connection properly. When the user is connected via a Remote Desktop Connection, video operations are transferred over the network connection to the client for display. Since networks have high latency and nowhere near the bandwidth of a local PCI or AGP bus, you need to adapt [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/32793","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=32793"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/32793\/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=32793"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=32793"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=32793"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}