{"id":11183,"date":"2011-03-18T07:00:00","date_gmt":"2011-03-18T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/03\/18\/you-can-extend-the-propsheetpage-structure-with-your-own-bonus-data\/"},"modified":"2011-03-18T07:00:00","modified_gmt":"2011-03-18T07:00:00","slug":"you-can-extend-the-propsheetpage-structure-with-your-own-bonus-data","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110318-00\/?p=11183","title":{"rendered":"You can extend the PROPSHEETPAGE structure with your own bonus data"},"content":{"rendered":"<p>\n<i>&#8230; for when regular strength lParam just isn&#8217;t enough.<\/i>\n<\/p>\n<p>\nA little-known and even less-used feature of the shell property sheet\nis that you can hang custom data off the end of the\n<code>PROPSHEETPAGE<\/code> structure,\nand the shell will carry it around for you.\nMind you, the shell carries it around by means of\n<code>memcpy<\/code> and destroys it by just freeing the\nunderlying memory,\nso whatever you stick on the end needs to be\n<a HREF=\"http:\/\/en.wikipedia.org\/wiki\/Plain_old_data_structures\">\nplain old data<\/a>.\n(Though you do get an opportunity to &#8220;construct&#8221; and &#8220;destruct&#8221;\nif you register a <code>PropSheetPageProc<\/code> callback,\nduring which you are permitted to modify your bonus data\nand the <code>lParam<\/code> field of the\n<code>PROPSHEETPAGE<\/code>.)\n<\/p>\n<p>\nHere&#8217;s a program that illustrates this technique.\nIt doesn&#8217;t do much interesting, mind you,\nbut maybe that&#8217;s a good thing: Makes for fewer distractions.\n<\/p>\n<pre>\n#include &lt;windows.h&gt;\n#include &lt;prsht.h&gt;\nHINSTANCE g_hinst;\nstruct ITEMPROPSHEETPAGE : public PROPSHEETPAGE\n{\n int cWidgets;\n TCHAR szItemName[100];\n};\n<\/pre>\n<p>\n<code>ITEMPROPSHEETPAGE<\/code> is a\ncustom structure that appends our bonus\ndata (an integer and a string) to the standard\n<code>PROPSHEETPAGE<\/code>.\nThis is the structure that our property sheet page will use.\n<\/p>\n<pre>\nINT_PTR CALLBACK DlgProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)\n{\n switch (uiMsg) {\n case WM_INITDIALOG:\n  {\n   ITEMPROPSHEETPAGE *ppsp =\n      reinterpret_cast&lt;ITEMPROPSHEETPAGE*&gt;(lParam));\n   SetDlgItemText(hwnd, 100, ppsp-&gt;szItemName);\n   SetDlgItemInt(hwnd, 101, ppsp-&gt;cWidgets, FALSE);\n  }\n  return TRUE;\n }\n return FALSE;\n}\n<\/pre>\n<p>\nThe <code>lParam<\/code> passed to <code>WM_INITDIALOG<\/code>\nis a pointer to the shell-managed copy of the <code>PROPSHEETPAGE<\/code>\nstructure.\nSince we associated this dialog procedure with a\n<code>ITEMPROPSHEETPAGE<\/code> structure,\nwe can cast it to the larger structure to get at our bonus data\n(which the shell happily <code>memcpy<\/code>&#8216;d from our copy\ninto the shell-managed copy).\n<\/p>\n<pre>\nHPROPSHEETPAGE CreateItemPropertySheetPage(\n    int cWidgets, PCTSTR pszItemName)\n{\n ITEMPROPSHEETPAGE psp;\n ZeroMemory(&amp;psp, sizeof(psp));\n psp.dwSize = sizeof(psp);\n psp.hInstance = g_hinst;\n psp.pszTemplate = MAKEINTRESOURCE(1);\n psp.pfnDlgProc = DlgProc;\n psp.cWidgets = cWidgets;\n lstrcpyn(psp.szItemName, pszItemName, 100);\n return CreatePropertySheetPage(&amp;psp);\n}\n<\/pre>\n<p>\nIt is here that we associate the <code>DlgProc<\/code>\nwith the <code>ITEMPROPSHEETPAGE<\/code>.\nJust to highlight that the pointer passed to the <code>DlgProc<\/code>\nis a copy of the <code>ITEMPROPSHEETPAGE<\/code> used to create\nthe property sheet page, I created a separate function to create\nthe property sheet page, so that the <code>ITEMPROPSHEETPAGE<\/code>\non the stack goes out of scope,\nmaking it clear that the copy passed to the <code>DlgProc<\/code>\nis not the one we passed to <code>CreatePropertySheetPage<\/code>.\n<\/p>\n<p>\nNote that you must set the <code>dwSize<\/code> of the\nbase <code>PROPSHEETPAGE<\/code>\nto the size of the\n<code>PROPSHEETPAGE<\/code> <i>plus<\/i> the size of your bonus data.\nIn other words, set it to the size of your <code>ITEMPROPSHEETPAGE<\/code>.\n<\/p>\n<pre>\nint WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,\n                   LPSTR lpCmdLine, int nCmdShow)\n{\n g_hinst = hinst;\n HPROPSHEETPAGE hpage =\n   CreateItemPropertySheetPage(42, TEXT(\"Elmo\"));\n if (hpage) {\n  PROPSHEETHEADER psh = { 0 };\n  psh.dwSize = sizeof(psh);\n  psh.dwFlags = PSH_DEFAULT;\n  psh.hInstance = hinst;\n  psh.pszCaption = TEXT(\"Item Properties\");\n  psh.nPages = 1;\n  psh.phpage = &amp;hpage;\n  PropertySheet(&amp;psh);\n }\n return 0;\n}\n<\/pre>\n<p>\nHere is where we display the property sheet.\nIt looks just like any other code that displays a property sheet.\nAll the magic happens in the way we created\nthe <code>HPROPSHEETPAGE<\/code>.\n<\/p>\n<p>\nIf you prefer to use the\n<code>PSH_PROPSHEETPAGE<\/code> flag, then the above code could have\nbeen written like this:\n<\/p>\n<pre>\nint WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,\n                   LPSTR lpCmdLine, int nCmdShow)\n{\n ITEMPROPSHEETPAGE psp;\n ZeroMemory(&amp;psp, sizeof(psp));\n psp.dwSize = sizeof(psp);\n psp.hInstance = g_hinst;\n psp.pszTemplate = MAKEINTRESOURCE(1);\n psp.pfnDlgProc = DlgProc;\n psp.cWidgets = cWidgets;\n lstrcpyn(psp.szItemName, pszItemName, 100);\n PROPSHEETHEADER psh = { 0 };\n psh.dwSize = sizeof(psh);\n psh.dwFlags = PSH_PROPSHEETPAGE;\n psh.hInstance = hinst;\n psh.pszCaption = TEXT(\"Item Properties\");\n psh.nPages = 1;\n psh.ppsp = &amp;psp;\n PropertySheet(&amp;psh);\n return 0;\n}\n<\/pre>\n<p>\nIf you want to create a property sheet with more than one page,\nthen you would pass an array of <code>ITEMPROPSHEETPAGE<\/code>s.\nNote that passing an array requires all the pages in the array\nto use the same custom structure (because that&#8217;s how arrays work;\nall the elements of an array are the same type).\n<\/p>\n<p>\nFinally, here&#8217;s the dialog template.\nPretty anticlimactic.\n<\/p>\n<pre>\n1 DIALOG 0, 0, PROP_SM_CXDLG, PROP_SM_CYDLG\nSTYLE WS_CAPTION | WS_SYSMENU\nCAPTION \"General\"\nFONT 8, \"MS Shell Dlg\"\nBEGIN\n    LTEXT \"Name:\",-1,7,11,42,14\n    LTEXT \"\",100,56,11,164,14\n    LTEXT \"Widgets:\",-1,7,38,42,14\n    LTEXT \"\",101,56,38,164,14\nEND\n<\/pre>\n<p>\nAnd there you have it.\nTacking custom data onto the end of a <code>PROPSHEETPAGE<\/code>,\nan alternative to\ntrying to cram everything into a single <code>lParam<\/code>.\n<\/p>\n<p>\n<b>Exercise<\/b>:\nObserve that the size of the <code>PROPSHEETPAGE<\/code> structure\nhas changed over time.\nFor example, the original <code>PROPSHEETPAGE<\/code> ends at the\n<code>pcRefParent<\/code>.\nIn Windows&nbsp;2000, there are two more fields,\nthe <code>pszHeaderTitle<\/code> and <code>pszHeaderSubTitle<\/code>.\nWindows&nbsp;XP added yet another field, the <code>hActCtx<\/code>.\nConsider a program written for Windows&nbsp;95 that uses this\ntechnique.\nHow does the shell know that the <code>cWidgets<\/code> is really\nbonus data and not a <code>pszHeaderTitle<\/code>?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>&#8230; for when regular strength lParam just isn&#8217;t enough. A little-known and even less-used feature of the shell property sheet is that you can hang custom data off the end of the PROPSHEETPAGE structure, and the shell will carry it around for you. Mind you, the shell carries it around by means of memcpy and [&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-11183","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>&#8230; for when regular strength lParam just isn&#8217;t enough. A little-known and even less-used feature of the shell property sheet is that you can hang custom data off the end of the PROPSHEETPAGE structure, and the shell will carry it around for you. Mind you, the shell carries it around by means of memcpy and [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/11183","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=11183"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/11183\/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=11183"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=11183"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=11183"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}