{"id":35743,"date":"2005-04-29T09:00:00","date_gmt":"2005-04-29T09:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2005\/04\/29\/building-a-dialog-template-at-run-time\/"},"modified":"2005-04-29T09:00:00","modified_gmt":"2005-04-29T09:00:00","slug":"building-a-dialog-template-at-run-time","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050429-00\/?p=35743","title":{"rendered":"Building a dialog template at run-time"},"content":{"rendered":"<p>\nWe&#8217;ve spent quite a bit of time over the past year\nlearning about dialog templates and the dialog manager.\nNow we&#8217;re going to put the pieces together to do something interesting:\nBuilding a dialog template on the fly.\n<\/p>\n<p>\nWhat we&#8217;re going to write is an extremely lame version of\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/winui\/winui\/windowsuserinterface\/windowing\/dialogboxes\/dialogboxreference\/dialogboxfunctions\/messagebox.asp\">\nthe <code>MessageBox<\/code> function<\/a>.\nWhy bother writing a bad version of something that Windows already does?\nBecause you can use it as a starting point for further enhancements.\nFor example, once you learn how to generate a template dynamically,\nyou can dynamically add buttons beyond the boring &#8220;OK&#8221; button,\nor you can add additional controls like a &#8220;Repeat this answer for all\nfuture occurrences of this dialog&#8221; checkbox or maybe insert\nan animation control.\n<\/p>\n<p>\nI&#8217;m going to start with a highly inefficient dialog template class.\nThis is not production-quality, but it&#8217;s good enough for didactic\npurposes.\n<\/p>\n<pre>\n#include &lt;vector&gt;\nclass DialogTemplate {\npublic:\n LPCDLGTEMPLATE Template() { return (LPCDLGTEMPLATE)&amp;v[0]; }\n void AlignToDword()\n  { if (v.size() % 4) Write(NULL, 4 - (v.size() % 4)); }\n void Write(LPCVOID pvWrite, DWORD cbWrite) {\n  v.insert(v.end(), cbWrite, 0);\n  if (pvWrite) CopyMemory(&amp;v[v.size() - cbWrite], pvWrite, cbWrite);\n }\n template&lt;typename T&gt; void Write(T t) { Write(&amp;t, sizeof(T)); }\n void WriteString(LPCWSTR psz)\n  { Write(psz, (lstrlenW(psz) + 1) * sizeof(WCHAR)); }\nprivate:\n vector&lt;BYTE&gt; v;\n};\n<\/pre>\n<p>\nI didn&#8217;t spend much time making this class look pretty because\nit&#8217;s not the focus of this article.  The <code>DialogTemplate<\/code>\nclass babysits a <code>vector<\/code> of bytes\nto which you can <code>Write<\/code> data.\nThere is also a little <code>AlignToDword<\/code> method that\npads the buffer to the next <code>DWORD<\/code> boundary.\nThis&#8217;ll come in handy, too.\n<\/p>\n<p>\nOur message box will need a dialog procedure\nwhich ends the dialog when the <code>IDCANCEL<\/code> button is pressed.\nIf we had made any enhancements to the dialog template, we would handle\nthem here as well.\n<\/p>\n<pre>\nINT_PTR CALLBACK DlgProc(HWND hwnd, UINT wm, WPARAM wParam, LPARAM lParam)\n{\n switch (wm) {\n case WM_INITDIALOG: return TRUE;\n case WM_COMMAND:\n  if (GET_WM_COMMAND_ID(wParam, lParam) == IDCANCEL) EndDialog(hwnd, 0);\n  break;\n }\n return FALSE;\n}\n<\/pre>\n<p>\nFinally, we build the template.  This is not hard, just tedious.\nOut of sheer laziness, we make the message box a fixed size.\nIf this were for a real program, we would have measured the text\n(using <code>ncm.lfCaptionFont<\/code>\nand <code>ncm.lfMessageFont<\/code>) to determine the\nbest size for the message box.\n<\/p>\n<pre>\nBOOL FakeMessageBox(HWND hwnd, LPCWSTR pszMessage, LPCWSTR pszTitle)\n{\n BOOL fSuccess = FALSE;\n HDC hdc = GetDC(NULL);\n if (hdc) {\n  NONCLIENTMETRICSW ncm = { sizeof(ncm) };\n  if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 0, &amp;ncm, 0)) {\n   DialogTemplate tmp;\n   \/\/ Write out the extended dialog template header\n   tmp.Write&lt;WORD&gt;(1); \/\/ dialog version\n   tmp.Write&lt;WORD&gt;(0xFFFF); \/\/ extended dialog template\n   tmp.Write&lt;DWORD&gt;(0); \/\/ help ID\n   tmp.Write&lt;DWORD&gt;(0); \/\/ extended style\n   tmp.Write&lt;DWORD&gt;(WS_CAPTION | WS_SYSMENU | DS_SETFONT | DS_MODALFRAME);\n   tmp.Write&lt;WORD&gt;(2); \/\/ number of controls\n   tmp.Write&lt;WORD&gt;(32); \/\/ X\n   tmp.Write&lt;WORD&gt;(32); \/\/ Y\n   tmp.Write&lt;WORD&gt;(200); \/\/ width\n   tmp.Write&lt;WORD&gt;(80); \/\/ height\n   tmp.WriteString(L\"\"); \/\/ no menu\n   tmp.WriteString(L\"\"); \/\/ default dialog class\n   tmp.WriteString(pszTitle); \/\/ title\n   \/\/ Next comes the font description.\n   \/\/ See text for discussion of fancy formula.\n   if (ncm.lfMessageFont.lfHeight &lt; 0) {\n     ncm.lfMessageFont.lfHeight = -MulDiv(ncm.lfMessageFont.lfHeight,\n              72, GetDeviceCaps(hdc, LOGPIXELSY));\n   }\n   tmp.Write&lt;WORD&gt;((WORD)ncm.lfMessageFont.lfHeight); \/\/ point\n   tmp.Write&lt;WORD&gt;((WORD)ncm.lfMessageFont.lfWeight); \/\/ weight\n   tmp.Write&lt;BYTE&gt;(ncm.lfMessageFont.lfItalic); \/\/ Italic\n   tmp.Write&lt;BYTE&gt;(ncm.lfMessageFont.lfCharSet); \/\/ CharSet\n   tmp.WriteString(ncm.lfMessageFont.lfFaceName);\n   \/\/ Then come the two controls.  First is the static text.\n   tmp.AlignToDword();\n   tmp.Write&lt;DWORD&gt;(0); \/\/ help id\n   tmp.Write&lt;DWORD&gt;(0); \/\/ window extended style\n   tmp.Write&lt;DWORD&gt;(WS_CHILD | WS_VISIBLE); \/\/ style\n   tmp.Write&lt;WORD&gt;(7); \/\/ x\n   tmp.Write&lt;WORD&gt;(7); \/\/ y\n   tmp.Write&lt;WORD&gt;(200-14); \/\/ width\n   tmp.Write&lt;WORD&gt;(80-7-14-7); \/\/ height\n   tmp.Write&lt;DWORD&gt;(-1); \/\/ control ID\n   tmp.Write&lt;DWORD&gt;(0x0082FFFF); \/\/ static\n   tmp.WriteString(pszMessage); \/\/ text\n   tmp.Write&lt;WORD&gt;(0); \/\/ no extra data\n   \/\/ Second control is the OK button.\n   tmp.AlignToDword();\n   tmp.Write&lt;DWORD&gt;(0); \/\/ help id\n   tmp.Write&lt;DWORD&gt;(0); \/\/ window extended style\n   tmp.Write&lt;DWORD&gt;(WS_CHILD | WS_VISIBLE |\n                    WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON); \/\/ style\n   tmp.Write&lt;WORD&gt;(75); \/\/ x\n   tmp.Write&lt;WORD&gt;(80-7-14); \/\/ y\n   tmp.Write&lt;WORD&gt;(50); \/\/ width\n   tmp.Write&lt;WORD&gt;(14); \/\/ height\n   tmp.Write&lt;DWORD&gt;(IDCANCEL); \/\/ control ID\n   tmp.Write&lt;DWORD&gt;(0x0080FFFF); \/\/ static\n   tmp.WriteString(L\"OK\"); \/\/ text\n   tmp.Write&lt;WORD&gt;(0); \/\/ no extra data\n   \/\/ Template is ready - go display it.\n   fSuccess = DialogBoxIndirect(g_hinst, tmp.Template(),\n                                hwnd, DlgProc) &gt;= 0;\n  }\n  ReleaseDC(NULL, hdc); \/\/ fixed 11 May\n }\n return fSuccess;\n}\n<\/pre>\n<p>\nThe fancy formula for determining the font point size is not that fancy\nafter all.  The dialog manager converts the font height from point to\npixels via\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/gdi\/fontext_1wmq.asp\">\nthe standard formula<\/a>:\n<\/p>\n<blockquote CLASS=\"m\"><p>\n<code>fontHeight = -MulDiv(pointSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);<\/code>\n<\/p><\/blockquote>\n<p>Therefore, to get the original pixel value back,\nwe need to solve this formula for <code>pointSize<\/code>\nso that when it is sent through the formula again, we get the\noriginal value back.\n<\/p>\n<p>\nThe template itself follows\n<a href=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/06\/23\/163596.aspx\">\nthe format we discussed earlier<\/a>, no surprises.\n<\/p>\n<p>\nOne subtlety is that the control identifier for our OK button\nis <code>IDCANCEL<\/code> instead of the <code>IDOK<\/code> you might\nhave expected.  That&#8217;s because this message box has only one button,\nso we want to\n<a href=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/12\/14\/300204.aspx\">\nlet the user hit the ESC key to dismiss it<\/a>.\n<\/p>\n<p>\nNow all that&#8217;s left to do is take this function for a little spin.\n<\/p>\n<pre>\nvoid OnChar(HWND hwnd, TCHAR ch, int cRepeat)\n{\n if (ch == TEXT(' ')) {\n  FakeMessageBox(hwnd,\n   L\"This is the text of a dynamically-generated dialog template. \"\n   L\"If Raymond had more time, this dialog would have looked prettier.\",\n   L\"Title of message box\");\n }\n}\n    \/\/ add to window procedure\n    HANDLE_MSG(hwnd, WM_CHAR, OnChar);\n<\/pre>\n<p>\nFire it up, hit the space bar, and observe the faux message box.\n<\/p>\n<p>\nOkay, so it&#8217;s not very exciting visually, but that wasn&#8217;t the point.\nThe point is that you now know how to build a dialog template at\nrun-time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We&#8217;ve spent quite a bit of time over the past year learning about dialog templates and the dialog manager. Now we&#8217;re going to put the pieces together to do something interesting: Building a dialog template on the fly. What we&#8217;re going to write is an extremely lame version of the MessageBox function. Why bother writing [&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-35743","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>We&#8217;ve spent quite a bit of time over the past year learning about dialog templates and the dialog manager. Now we&#8217;re going to put the pieces together to do something interesting: Building a dialog template on the fly. What we&#8217;re going to write is an extremely lame version of the MessageBox function. Why bother writing [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/35743","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=35743"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/35743\/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=35743"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=35743"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=35743"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}