Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 4

The Old New Thing (Raymond Chen) News

Summary

Raymond Chen continues his series on creating a fake agile wrapper in COM, explaining how to move non-marshalable objects by forwarding references into the force_marshal wrapper and discussing C++/WinRT deduction guides.

<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> <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> wrapper.</p> <pre>template&lt;typename Smart&gt; struct force_marshal : winrt::implements&lt;force_marshal&lt;Smart&gt;, IUnknown, winrt::non_agile&gt; { <span style="border: solid 1px currentcolor; border-bottom: none;">template&lt;typename Arg&gt; </span> <span style="border: solid 1px currentcolor; border-top: none;">force_marshal(Arg&amp;&amp; arg) : m_p(std::forward&lt;Arg&gt;(arg)) {}</span> Smart m_p; }; </pre> <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> <p>Now it&#8217;s a matter of plumbing this reference all the way down.</p> <pre>template&lt;typename T&gt; struct fake_agile_ref { ⟦ ... ⟧ <span style="border: solid 1px currentcolor;">template&lt;typename Arg&gt;</span> fake_agile_ref(<span style="border: solid 1px currentcolor;">Arg&amp;&amp;</span> p) : m_raw(winrt::get_abi(p)) { if (m_raw) { m_context = winrt::capture&lt;IContextCallback&gt;(CoGetObjectContext); m_token = get_context_token(); m_git = winrt::create_instance&lt;IGlobalInterfaceTable&gt;(CLSID_StdGlobalInterfaceTable); winrt::check_hresult(m_git-&gt;RegisterInterfaceInGlobal( winrt::make&lt;force_marshal&lt;Smart&gt;&gt;(<span style="border: solid 1px currentcolor;">std::forward&lt;Arg&gt;(p)</span>).get(), __uuidof(IUnknown), &amp;m_cookie)); } } ⟦ ... ⟧ }; template&lt;typename Delegate&gt; std::remove_reference_t&lt;Delegate&gt; make_agile_delegate(Delegate&amp;&amp; d) { if (d.try_as&lt;::IAgileObject&gt;()) { return d; } if (d.try_as&lt;::INoMarshal&gt;()) { return [agile = fake_agile_ref(<span style="border: solid 1px currentcolor;">std::forward&lt;Delegate&gt;(d)</span>](auto&amp;&amp;...args) { return agile.get()(std::forward&lt;decltype(args)&gt;(args)...); }; } return [agile = winrt::agile_ref(d)](auto&amp;&amp;...args) { return agile.get()(std::forward&lt;decltype(args)&gt;(args)...); }; } </pre> <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> <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> <p>Yeah, so maybe I lied.</p> <p>We&#8217;ll look at it next time.</p> <p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260806-00/?p=112595">Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 4</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
Original Article
View Cached Full Text

Cached at: 08/07/26, 01:37 PM

# Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 4 - The Old New Thing Source: [https://devblogs.microsoft.com/oldnewthing/20260806-00?p=112595](https://devblogs.microsoft.com/oldnewthing/20260806-00?p=112595) Last time, we successfully[our plan to use the global interface table to hold created a fake agile wrapper that is technically agile, even though it isn’t useful outside its home apartment](https://devblogs.microsoft.com/oldnewthing/20260805-00/?p=112591)\. I noted that there are opportunities for fine\-tuning\. 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`force\_marshal`wrapper\. ``` template<typename Smart> struct force_marshal : winrt::implements<force_marshal<Smart>, IUnknown, winrt::non_agile> { template<typename Arg> force_marshal(Arg&& arg) : m_p(std::forward<Arg>(arg)) {} Smart m_p; }; ``` The`force\_marshal<Smart\>`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`force\_marshal<Smart\>`object rather than copied\. Now it’s a matter of plumbing this reference all the way down\. ``` template<typename T> struct fake_agile_ref { ⟦ ... ⟧ template<typename Arg> fake_agile_ref(Arg&& p) : m_raw(winrt::get_abi(p)) { if (m_raw) { m_context = winrt::capture<IContextCallback>(CoGetObjectContext); m_token = get_context_token(); m_git = winrt::create_instance<IGlobalInterfaceTable>(CLSID_StdGlobalInterfaceTable); winrt::check_hresult(m_git->RegisterInterfaceInGlobal( winrt::make<force_marshal<Smart>>(std::forward<Arg>(p)).get(), __uuidof(IUnknown), &m_cookie)); } } ⟦ ... ⟧ }; template<typename Delegate> std::remove_reference_t<Delegate> make_agile_delegate(Delegate&& d) { if (d.try_as<::IAgileObject>()) { return d; } if (d.try_as<::INoMarshal>()) { return [agile = fake_agile_ref(std::forward<Delegate>(d)](auto&&...args) { return agile.get()(std::forward<decltype(args)>(args)...); }; } return [agile = winrt::agile_ref(d)](auto&&...args) { return agile.get()(std::forward<decltype(args)>(args)...); }; } ``` Note that we didn’t have to update the deduction guides for`fake\_agile\_ref`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`fake\_agile\_ref`from an rvalue reference, the deduction guide for`const&`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\. Remember how I complained that we couldn’t use`std::unique\_ptr`to avoid a lot of boilerplate in`fake\_agile\_ref`to manage the fact that cookies cannot be copied? Yeah, so maybe I lied\. We’ll look at it next time\. ### Category ### Topics ## Author ![Raymond Chen](https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2019/02/RaymondChen_5in-150x150.jpg) Raymond has been involved in the evolution of Windows for more than 30 years\. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie\-jeebies\. The Web site spawned a book, coincidentally also titled The Old New Thing \(Addison Wesley 2007\)\. He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information\.

Similar Articles