{"id":112595,"date":"2026-08-06T07:00:00","date_gmt":"2026-08-06T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112595"},"modified":"2026-08-06T22:57:28","modified_gmt":"2026-08-07T05:57:28","slug":"20260806-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260806-00\/?p=112595","title":{"rendered":"Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 4"},"content":{"rendered":"<p>Last time, we successfully <a title=\"Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 3\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260805-00\/?p=112591\"> our plan to use the global interface table to hold created a fake agile wrapper that is technically agile, even though it isn&#8217;t useful outside its home apartment<\/a>. I noted that there are opportunities for fine-tuning.<\/p>\n<p>One thing we can do is move the non-marshalable COM object rather than copying it. This means forwarding the reference all the way into the <code>force_<wbr \/>marshal<\/code>\u00a0wrapper.<\/p>\n<pre>template&lt;typename Smart&gt;\r\nstruct force_marshal :\r\n    winrt::implements&lt;force_marshal&lt;Smart&gt;, IUnknown, winrt::non_agile&gt;\r\n{\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">template&lt;typename Arg&gt;                                   <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">force_marshal(Arg&amp;&amp; arg) : m_p(std::forward&lt;Arg&gt;(arg)) {}<\/span>\r\n    Smart m_p;\r\n};\r\n<\/pre>\n<p>The <code>force_<wbr \/>marshal&lt;Smart&gt;<\/code> now takes anything and forwards it into the smart pointer. This means that if the inbound parameter is an rvalue reference to a smart pointer, the COM reference is moved into the <code>force_<wbr \/>marshal&lt;Smart&gt;<\/code> object rather than copied.<\/p>\n<p>Now it&#8217;s a matter of plumbing this reference all the way down.<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct fake_agile_ref\r\n{\r\n    \u27e6 ... \u27e7\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">template&lt;typename Arg&gt;<\/span>\r\n    fake_agile_ref(<span style=\"border: solid 1px currentcolor;\">Arg&amp;&amp;<\/span> p) : m_raw(winrt::get_abi(p))\r\n    {\r\n        if (m_raw) {\r\n            m_context = winrt::capture&lt;IContextCallback&gt;(CoGetObjectContext);\r\n            m_token = get_context_token();\r\n            m_git = winrt::create_instance&lt;IGlobalInterfaceTable&gt;(CLSID_StdGlobalInterfaceTable);\r\n            winrt::check_hresult(m_git-&gt;RegisterInterfaceInGlobal(\r\n                winrt::make&lt;force_marshal&lt;Smart&gt;&gt;(<span style=\"border: solid 1px currentcolor;\">std::forward&lt;Arg&gt;(p)<\/span>).get(),\r\n                __uuidof(IUnknown), &amp;m_cookie));\r\n        }\r\n    }\r\n\r\n    \u27e6 ... \u27e7\r\n};\r\n\r\ntemplate&lt;typename Delegate&gt;\r\nstd::remove_reference_t&lt;Delegate&gt; make_agile_delegate(Delegate&amp;&amp; d)\r\n{\r\n    if (d.try_as&lt;::IAgileObject&gt;()) {\r\n        return d;\r\n    }\r\n\r\n    if (d.try_as&lt;::INoMarshal&gt;()) {\r\n        return [agile = fake_agile_ref(<span style=\"border: solid 1px currentcolor;\">std::forward&lt;Delegate&gt;(d)<\/span>](auto&amp;&amp;...args) {\r\n            return agile.get()(std::forward&lt;decltype(args)&gt;(args)...);\r\n        };\r\n    }\r\n\r\n    return [agile = winrt::agile_ref(d)](auto&amp;&amp;...args) {\r\n        return agile.get()(std::forward&lt;decltype(args)&gt;(args)...);\r\n    };\r\n}\r\n<\/pre>\n<p>Note that we didn&#8217;t have to update the deduction guides for <code>fake_<wbr \/>agile_<wbr \/>ref<\/code> to add forwarding support. Deduction guides are matched against the constructor invocation to determine which template specialization to use, but they are not used for actually invoking the constructor. That happens by matching against the constructors themselves. So if somebody tries to create a <code>fake_<wbr \/>agile_<wbr \/>ref<\/code> from an rvalue reference, the deduction guide for <code>const&amp;<\/code> steers class template argument deduction (CTAD) toward the correct specialization, and then when the compiler actually looks for a constructor, it finds the one that takes an rvalue reference.<\/p>\n<p>Remember how I complained that we couldn&#8217;t use <code>std::<wbr \/>unique_ptr<\/code> to avoid a lot of boilerplate in <code>fake_<wbr \/>agile_<wbr \/>ref<\/code> to manage the fact that cookies cannot be copied?<\/p>\n<p>Yeah, so maybe I lied.<\/p>\n<p>We&#8217;ll look at it next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Moving things around if possible.<\/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-112595","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Moving things around if possible.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112595","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=112595"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112595\/revisions"}],"predecessor-version":[{"id":112596,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112595\/revisions\/112596"}],"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=112595"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112595"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112595"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}