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

The Old New Thing (Raymond Chen) News

Summary

Raymond Chen continues his series on creating a fake agile wrapper for non-marshalable COM objects by wrapping them in a marshalable object, enabling registration in the global interface table while preserving undefined-behavior-free failures in other apartments.

<p>Last time, we tried to execute on <a title="Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 2" href="https://devblogs.microsoft.com/oldnewthing/20260804-00/?p=112586"> our plan to use the global interface table to hold hold a reference to an object in another apartment that automatically expires when the apartment runs down</a>. But it broke down because objects that are marked <code>INoMarshal</code> can&#8217;t go into the global interface table.</p> <p>So we will just force the square peg into the round hole: We can put the non-marshalable object inside an object that <i>is</i> marshalable.</p> <pre>template&lt;typename Smart&gt; struct force_marshal : winrt::implements&lt;force_marshal&lt;Smart&gt;, ::IUnknown, winrt::non_agile&gt; { force_marshal(Smart const&amp; p) : m_p(p) {} Smart m_p; }; </pre> <p>The <code>force_marshal&lt;Smart&gt;</code> object babysits a non-marshalable smart pointer to a COM object and exposes a marshalable wrapper around it. Since the wrapped object is not agile, the wrapper cannot be either. (If the wrapper were agile, then we&#8217;d be back where we started: How do we ensure that the <code>m_p</code> is destructed in the correct apartment?)¹</p> <p>We can put the unmarshalable object inside the wrapper, and then put the wrapper in the global interface table.</p> <pre>template&lt;typename T&gt; struct fake_agile_ref { ⟦ ... ⟧ fake_agile_ref(Smart const&amp; 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( <span style="border: solid 1px currentcolor;">winrt::make&lt;force_marshal&lt;Smart&gt;&gt;(p).get()</span>, __uuidof(IUnknown), &amp;m_cookie)); } } ⟦ ... ⟧ }; template&lt;typename T&gt; fake_agile_ref(winrt::com_ptr&lt;T&gt; const&amp;) -&gt; fake_agile_ref&lt;T&gt;; template&lt;typename T&gt; fake_agile_ref(T const&amp;) -&gt; fake_agile_ref&lt;T&gt;; </pre> <p>Okay, so now we have managed to create an agile wrapper around an unmarshalable object. This agile wrapper is agile on paper: You can use it from any thread. However, it is not agile in practice: If you try to use it from the wrong apartment, it throws an exception. But at least the behavior when used from the wrong apartment is <i>well-defined</i>, as opposed to the case of directly using an unmarshalable object from the wrong apartment, which is <i>undefined</i>.</p> <p>Next time, we&#8217;ll do some fine tuning.</p> <p><b>Bonus chatter</b>: Of course, now that we have a marshalable wrapper, we <i>could</i> use that wrapper to call the original non-marshalable object.</p> <table class="cp3" style="border-collapse: collapse; text-align: center;" border="0" cellspacing="0" cellpadding="3"> <tbody> <tr> <td>Original apartment</td> <td style="border-right: dashed currentcolor 1px;"> </td> <td>&nbsp;</td> <td>Other apartment</td> </tr> <tr> <td style="border: solid currentcolor 1px;">Wrapper</td> <td style="border-right: dashed currentcolor 1px;">←</td> <td>←</td> <td style="border: solid currentcolor 1px;">Caller</td> </tr> <tr> <td>↓</td> <td style="border-right: dashed currentcolor 1px;"> </td> <td>&nbsp;</td> </tr> <tr> <td style="border: solid currentcolor 1px;">Non-marshalable</td> <td style="border-right: dashed currentcolor 1px;"> </td> <td>&nbsp;</td> </tr> </tbody> </table> <p>The non-marshalable object does not allow any arrows to come in from other apartments, but the wrapper lives in the same apartment as the non-marshalable object, so <i>its</i> arrow is coming from within the same apartment.</p> <p>You can think of the wrapper as a VPN into the original apartment, allowing calls to come in from the outside, but to appear to the non-marshalable object as if they came from within the same apartment.</p> <p>Now, you <i>could</i> do that, but I&#8217;m not going to. The original object presumably went out of its way to declare itself non-marshalable for a reason, so we honor that preference and not play funny games to trick it into doing something it said that it didn&#8217;t want to do.</p> <p>¹ Two commenters fell into this trap by suggesting that we wrap the non-marshalable object inside an object that implements <code>IMarshal</code>. If you implement <code>IMarshal</code>, then you are saying, &#8220;I&#8217;m way cooler than a standard non-agile object. I&#8217;m going to do fancy stuff (like being agile).&#8221; But we <i>want</i> to be a boring non-agile object, so that COM will do standard marshaling for us.</p> <p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260805-00/?p=112591">Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 3</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/06/26, 01:35 PM

# Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 3 - The Old New Thing Source: [https://devblogs.microsoft.com/oldnewthing/20260805-00?p=112591](https://devblogs.microsoft.com/oldnewthing/20260805-00?p=112591) Last time, we tried to execute on[our plan to use the global interface table to hold hold a reference to an object in another apartment that automatically expires when the apartment runs down](https://devblogs.microsoft.com/oldnewthing/20260804-00/?p=112586)\. But it broke down because objects that are marked`INoMarshal`can’t go into the global interface table\. So we will just force the square peg into the round hole: We can put the non\-marshalable object inside an object that*is*marshalable\. ``` template<typename Smart> struct force_marshal : winrt::implements<force_marshal<Smart>, ::IUnknown, winrt::non_agile> { force_marshal(Smart const& p) : m_p(p) {} Smart m_p; }; ``` The`force\_marshal<Smart\>`object babysits a non\-marshalable smart pointer to a COM object and exposes a marshalable wrapper around it\. Since the wrapped object is not agile, the wrapper cannot be either\. \(If the wrapper were agile, then we’d be back where we started: How do we ensure that the`m\_p`is destructed in the correct apartment?\)¹ We can put the unmarshalable object inside the wrapper, and then put the wrapper in the global interface table\. ``` template<typename T> struct fake_agile_ref { ⟦ ... ⟧ fake_agile_ref(Smart const& 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>>(p).get(), __uuidof(IUnknown), &m_cookie)); } } ⟦ ... ⟧ }; template<typename T> fake_agile_ref(winrt::com_ptr<T> const&) -> fake_agile_ref<T>; template<typename T> fake_agile_ref(T const&) -> fake_agile_ref<T>; ``` Okay, so now we have managed to create an agile wrapper around an unmarshalable object\. This agile wrapper is agile on paper: You can use it from any thread\. However, it is not agile in practice: If you try to use it from the wrong apartment, it throws an exception\. But at least the behavior when used from the wrong apartment is*well\-defined*, as opposed to the case of directly using an unmarshalable object from the wrong apartment, which is*undefined*\. Next time, we’ll do some fine tuning\. **Bonus chatter**: Of course, now that we have a marshalable wrapper, we*could*use that wrapper to call the original non\-marshalable object\. Original apartmentOther apartmentWrapper←←Caller↓Non\-marshalableThe non\-marshalable object does not allow any arrows to come in from other apartments, but the wrapper lives in the same apartment as the non\-marshalable object, so*its*arrow is coming from within the same apartment\. You can think of the wrapper as a VPN into the original apartment, allowing calls to come in from the outside, but to appear to the non\-marshalable object as if they came from within the same apartment\. Now, you*could*do that, but I’m not going to\. The original object presumably went out of its way to declare itself non\-marshalable for a reason, so we honor that preference and not play funny games to trick it into doing something it said that it didn’t want to do\. ¹ Two commenters fell into this trap by suggesting that we wrap the non\-marshalable object inside an object that implements`IMarshal`\. If you implement`IMarshal`, then you are saying, “I’m way cooler than a standard non\-agile object\. I’m going to do fancy stuff \(like being agile\)\.” But we*want*to be a boring non\-agile object, so that COM will do standard marshaling for us\. ### 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