{"id":105967,"date":"2021-11-25T07:00:00","date_gmt":"2021-11-25T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105967"},"modified":"2021-11-25T06:16:48","modified_gmt":"2021-11-25T14:16:48","slug":"20211125-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20211125-00\/?p=105967","title":{"rendered":"How do I pass an array of variable-sized <CODE>PROPSHEETPAGE<\/CODE> structures to <CODE>PropertySheet<\/CODE>?"},"content":{"rendered":"<p>Last time, we noted that <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20211124-00\/?p=105961\"> you can add your own custom data to the end of the <code>PROPSHEETPAGE<\/code><\/a>, and if you set the <code>dwSize<\/code> to include the custom data, then the system will copy that custom data into the <code>HPROPSHEETPAGE<\/code>.<\/p>\n<p>This technique comes in handy if you need to create a property sheet page with <code>Create\u00adProp\u00adSheet\u00adPage<\/code>, since it gives you a way to store more data than just the single <code>lParam<\/code> that comes with the <code>PROPSHEETPAGE<\/code> structure.<\/p>\n<p>When you fill out a <code>PROPSHEETHEADER<\/code> structure, you can choose whether you&#8217;re passing an array of <code>HPROPSHEETPAGE<\/code> handles (created by <code>Create\u00adProp\u00adSheet\u00adPage<\/code>) or an array of <code>PROPSHEETPAGE<\/code> structures. Passing an array of <code>HPROPSHEETPAGE<\/code> handles isn&#8217;t a problem, since all <code>HPROPSHEETPAGE<\/code> handles are the same size, regardless of the size of the <code>PROPSHEETPAGE<\/code> lurking inside them. But passing an array of variable-sized <code>PROPSHEETPAGE<\/code> structures is a trickier business.<\/p>\n<p>What we want to do is lay out the memory like this:<\/p>\n<p><!-- \"margin-left: 1px\" to work around devblogs.microsoft.com default style sheet --><\/p>\n<table style=\"border-collapse: collapse; margin-left: 1px;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\" rowspan=\"2\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>page1.dwSize<\/code><\/span><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>PROPSHEETPAGE<\/code><\/span><\/td>\n<td style=\"border: solid 1px black; padding: 3px;\"><code>dwSize<\/code><br \/>\n<code>dwFlags<\/code><br \/>\n\u22ee<br \/>\n<code>lParam<\/code><br \/>\n\u22ee<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; padding: 3px;\">page1<br \/>\n\u00a0extra<br \/>\n\u00a0\u00a0data<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\" rowspan=\"2\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>page2.dwSize<\/code><\/span><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>PROPSHEETPAGE<\/code><\/span><\/td>\n<td style=\"border: solid 1px black; padding: 3px;\"><code>dwSize<\/code><br \/>\n<code>dwFlags<\/code><br \/>\n\u22ee<br \/>\n<code>lParam<\/code><br \/>\n\u22ee<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; padding: 3px;\">page2<br \/>\n\u00a0extra<br \/>\n\u00a0\u00a0data<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\" rowspan=\"2\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>page3.dwSize<\/code><\/span><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>PROPSHEETPAGE<\/code><\/span><\/td>\n<td style=\"border: solid 1px black; padding: 3px;\"><code>dwSize<\/code><br \/>\n<code>dwFlags<\/code><br \/>\n\u22ee<br \/>\n<code>lParam<\/code><br \/>\n\u22ee<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; padding: 3px;\">page3<br \/>\n\u00a0extra<br \/>\n\u00a0\u00a0data<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>We can do this by manufacturing a structure to hold the three extended <code>PROPSHEETPAGE<\/code> structures.<\/p>\n<pre>struct ThreePages\r\n{\r\n    Page1Data page1;\r\n    Page2Data page2;\r\n    Page3Data page3;\r\n};\r\n\r\nThreePages pages;\r\n<\/pre>\n<p>The na\u00efve say of setting the <code>dwSize<\/code> members is to set each one to the size of the corresponding structure.<\/p>\n<pre>pages.page1.dwSize = sizeof(pages.page1);\r\npages.page2.dwSize = sizeof(pages.page2);\r\npages.page3.dwSize = sizeof(pages.page3);\r\n<\/pre>\n<p>This assumes that the three structures can be laid out next to each other without any inter-member padding. But that may not be true if the structures have different alignment requirements, say, if one of them contains a <code>__mi128<\/code>.<\/p>\n<table style=\"border-collapse: collapse; margin-left: 1px;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\" rowspan=\"2\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>sizeof(page1)<\/code><\/span><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>PROPSHEETPAGE<\/code><\/span><\/td>\n<td style=\"border: solid 1px black; padding: 3px;\"><code>dwSize<\/code><br \/>\n<code>dwFlags<\/code><br \/>\n\u22ee<br \/>\n<code>lParam<\/code><br \/>\n\u22ee<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; padding: 3px;\">page1<br \/>\n\u00a0extra<br \/>\n\u00a0\u00a0data<\/td>\n<\/tr>\n<tr>\n<td colspan=\"2\">oops<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; padding: 3px;\">(padding?)<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\" rowspan=\"2\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>sizeof(page2)<\/code><\/span><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>PROPSHEETPAGE<\/code><\/span><\/td>\n<td style=\"border: solid 1px black; padding: 3px;\"><code>dwSize<\/code><br \/>\n<code>dwFlags<\/code><br \/>\n\u22ee<br \/>\n<code>lParam<\/code><br \/>\n\u22ee<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; padding: 3px;\">page2<br \/>\n\u00a0extra<br \/>\n\u00a0\u00a0data<\/td>\n<\/tr>\n<tr>\n<td colspan=\"2\">oops<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; padding: 3px;\">(padding?)<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\" rowspan=\"2\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>sizeof(page3)<\/code><\/span><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>PROPSHEETPAGE<\/code><\/span><\/td>\n<td style=\"border: solid 1px black; padding: 3px;\"><code>dwSize<\/code><br \/>\n<code>dwFlags<\/code><br \/>\n\u22ee<br \/>\n<code>lParam<\/code><br \/>\n\u22ee<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; padding: 3px;\">page3<br \/>\n\u00a0extra<br \/>\n\u00a0\u00a0data<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; padding: 3px;\">(padding?)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In the presence of padding, we have a shortfall between the size of each page and the start of the next page, resulting in an &#8220;oops&#8221; gap highlighted above.<\/p>\n<p>In order to accommodate varying alignment requirements, the <code>dwSize<\/code> must include the padding so that the property sheet manager can find the next structure.\u00b9 I&#8217;ve marked some key addresses in the diagram below:<\/p>\n<table style=\"border-collapse: collapse; margin-left: 1px;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\" rowspan=\"3\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>page1.dwSize<\/code><\/span><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>PROPSHEETPAGE<\/code><\/span><\/td>\n<td style=\"border: solid 1px black; padding: 3px;\"><code>dwSize<\/code><br \/>\n<code>dwFlags<\/code><br \/>\n\u22ee<br \/>\n<code>lParam<\/code><br \/>\n\u22ee<\/td>\n<td style=\"position: relative; top: -1ex;\" valign=\"top\">\u2190 <code>&amp;page1<\/code><\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; padding: 3px;\">page1<br \/>\n\u00a0extra<br \/>\n\u00a0\u00a0data<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; padding: 3px;\">(padding?)<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\" rowspan=\"2\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>page2.dwSize<\/code><\/span><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>PROPSHEETPAGE<\/code><\/span><\/td>\n<td style=\"border: solid 1px black; padding: 3px;\"><code>dwSize<\/code><br \/>\n<code>dwFlags<\/code><br \/>\n\u22ee<br \/>\n<code>lParam<\/code><br \/>\n\u22ee<\/td>\n<td style=\"position: relative; top: -1ex;\" valign=\"top\">\u2190 <code>&amp;page2<\/code><\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; padding: 3px;\">page2<br \/>\n\u00a0extra<br \/>\n\u00a0\u00a0data<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\" rowspan=\"2\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>page3.dwSize<\/code><\/span><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; border-right: none; vertical-align: middle; width: 1em;\"><span style=\"writing-mode: vertical-lr; -ms-writing-mode: tb-lr; transform: rotate(180deg);\"><code>PROPSHEETPAGE<\/code><\/span><\/td>\n<td style=\"border: solid 1px black; padding: 3px;\"><code>dwSize<\/code><br \/>\n<code>dwFlags<\/code><br \/>\n\u22ee<br \/>\n<code>lParam<\/code><br \/>\n\u22ee<\/td>\n<td style=\"position: relative; top: -1ex;\" valign=\"top\">\u2190 <code>&amp;page3<\/code><\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; padding: 3px;\">page3<br \/>\n\u00a0extra<br \/>\n\u00a0\u00a0data<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"position: relative; top: -1ex;\" valign=\"top\">\u2190 <code>&amp;pages + 1<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<pre>pages.page1.dwSize = static_cast&lt;DWORD&gt;(\r\n    reinterpret_cast&lt;DWORD_PTR&gt;(std::addressof(pages.page2)) -\r\n    reinterpret_cast&lt;DWORD_PTR&gt;(std::addressof(pages.page1)));\r\npages.page2.dwSize = static_cast&lt;DWORD&gt;(\r\n    reinterpret_cast&lt;DWORD_PTR&gt;(std::addressof(pages.page3)) -\r\n    reinterpret_cast&lt;DWORD_PTR&gt;(std::addressof(pages.page2)));\r\npages.page3.dwSize = static_cast&lt;DWORD&gt;(\r\n    reinterpret_cast&lt;DWORD_PTR&gt;(std::addressof(pages + 1)) -\r\n    reinterpret_cast&lt;DWORD_PTR&gt;(std::addressof(pages.page3)));\r\n<\/pre>\n<p>This is quite a mouthful, but the idea is that we want to measure the distance to the next thing. We use <code>std::addressof<\/code> instead of the traditional <code>&amp;<\/code> operator to protect against the possibility that the <code>&amp;<\/code> operator has been overloaded.\u00b2<\/p>\n<p>Yes, this is quite annoying, but it&#8217;s also probably not something you&#8217;re likely to be doing, because you could just use a pointer to a stack-allocated object which will remain valid until <code>Property\u00adSheet<\/code> returns. The main value of the <code>PROPSHEETPAGE<\/code> payload is in the case where you need to produce an <code>HPROPSHEETPAGE<\/code>, since the <code>HPROPSHEETPAGE<\/code> is probably going to outlive any stack variables.<\/p>\n<p>But it&#8217;s there if you need it.<\/p>\n<p>\u00b9 Don&#8217;t even think of using <code>#pragma pack(1)<\/code> to remove the padding. This will misalign the next structure and result in crashes on alignment-sensitive platforms.<\/p>\n<p>\u00b2 Overloading the <code>&amp;<\/code> operator is something that annoys C++ library authors, although it&#8217;s still nowhere as annoying as <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200904-00\/?p=104172\"> overloading the comma operator<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s &#8220;just&#8221; an array of variable-sized structures.<\/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-105967","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It&#8217;s &#8220;just&#8221; an array of variable-sized structures.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105967","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=105967"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105967\/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=105967"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105967"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105967"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}