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

The Old New Thing (Raymond Chen) Tools

Summary

Raymond Chen explains how to use std::unique_ptr to manage a registration cookie in a fake agile wrapper, discussing integer-to-pointer round-tripping and the wil::unique_any alternative.

<p>Last time, I confessed that <a title="Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 4" href="https://devblogs.microsoft.com/oldnewthing/20260806-00/?p=112595"> I lied when i said that we can&#8217;t use <code>std::<wbr />unique_ptr</code> to manage the registration cookie</a>.</p> <p>The trick here is that the registration cookie is of type <code>DWORD</code>, which fits in a pointer, so we can smuggle the integer value inside a pointer.</p> <pre>template&lt;typename T&gt; struct fake_agile_ref { private: using Smart = std::conditional_t&lt; std::is_base_of_v&lt;winrt::Windows::Foundation::IUnknown, T&gt;, T, winrt::com_ptr&lt;T&gt;&gt;; <span style="border: solid 1px currentcolor; border-bottom: none;">struct git_deleter </span> <span style="border: 1px currentcolor; border-style: none solid;">{ </span> <span style="border: 1px currentcolor; border-style: none solid;"> winrt::com_ptr&lt;IGlobalInterfaceTable&gt; m_git; </span> <span style="border: 1px currentcolor; border-style: none solid;">  </span> <span style="border: 1px currentcolor; border-style: none solid;"> void operator()(void* p) </span> <span style="border: 1px currentcolor; border-style: none solid;"> { </span> <span style="border: 1px currentcolor; border-style: none solid;"> m_git-&gt;RevokeInterfaceFromGlobal(static_cast&lt;DWORD&gt;(reinterpret_cast&lt;uintptr_t&gt;(p)));</span> <span style="border: 1px currentcolor; border-style: none solid;"> } </span> <span style="border: solid 1px currentcolor; border-top: none;">}; </span> winrt::com_ptr&lt;IContextCallback&gt; m_context; ULONG_PTR m_token = 0; <span style="border: solid 1px currentcolor;">std::unique_ptr&lt;void, git_deleter&gt; m_cookie;</span> void* m_raw = nullptr; </pre> <p>Our custom deleter holds a pointer to the Global Interface Table and uses it to revoke the cookie on destruction. The cookie is an integer smuggled inside a pointer, so we cast the pointer back to an integer by passing through a <code>uintptr_t</code> to avoid a compiler warning about casting between an integer and pointer of different sizes.</p> <p>We are relying on the fact that Windows implementations are required to support round-tripping integers through pointers. Macros like <code>MAKEINTRESOURCE</code> rely on it. It&#8217;s also codified in Windows with helper functions like <code>PtrToInt</code> and <code>IntToPtr</code>, but I&#8217;m writing it out for expository purposes rather than using those helpers.</p> <p>We can then store the Global Interface Table pointer and the corresponding cookie in the <code>unique_ptr</code>:</p> <pre> 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(); <span style="border: solid 1px currentcolor; border-bottom: none;">auto&amp; git = m_cookie.get_deleter().m_git; </span> <span style="border: 1px currentcolor; border-style: none solid;">git = winrt::create_instance&lt;IGlobalInterfaceTable&gt;(CLSID_StdGlobalInterfaceTable);</span> <span style="border: solid 1px currentcolor; border-top: none;">DWORD cookie; </span> winrt::check_hresult(<span style="border: solid 1px currentcolor;">git</span>-&gt;RegisterInterfaceInGlobal( winrt::make&lt;force_marshal&lt;Smart&gt;&gt;(p).get(), __uuidof(IUnknown), &amp;m_cookie)); <span style="border: solid 1px currentcolor;">m_cookie.reset(reinterpret_cast&lt;void*&gt;(static_cast&lt;uintptr_t&gt;(cookie)));</span> } } </pre> <p>And now that we are letting <code>unique_ptr</code> manage the lifetime of the cookie, we don&#8217;t need a custom destructor, which allows us to use the Rule of Zero and simply not have any copy or move constructors or assignment operators.</p> <pre> // <span style="text-decoration: line-through;">fake_agile_ref(fake_agile_ref&amp;&amp; other) noexcept :</span> // <span style="text-decoration: line-through;"> m_context(std::move(other.m_context)),</span> // <span style="text-decoration: line-through;"> m_token(std:exchange(other.m_token, 0)),</span> // <span style="text-decoration: line-through;"> m_git(std::move(other.m_git)),</span> // <span style="text-decoration: line-through;"> m_cookie(std::exchange(other.m_cookie, 0)),</span> // <span style="text-decoration: line-through;"> m_raw(other.m_raw)</span> // <span style="text-decoration: line-through;">{</span> // <span style="text-decoration: line-through;">}</span> // <span style="text-decoration: line-through;">fake_agile_ref&amp; operator=(fake_agile_ref&amp;&amp; other) noexcept</span> // <span style="text-decoration: line-through;">{</span> // <span style="text-decoration: line-through;"> using std::swap;</span> // <span style="text-decoration: line-through;"> swap(m_context, other.m_context);</span> // <span style="text-decoration: line-through;"> swap(m_token, other.m_token);</span> // <span style="text-decoration: line-through;"> swap(m_git, other.m_git);</span> // <span style="text-decoration: line-through;"> swap(m_cookie, other.m_cookie);</span> // <span style="text-decoration: line-through;"> swap(m_raw, other.m_raw);</span> // <span style="text-decoration: line-through;">}</span> // <span style="text-decoration: line-through;">~fake_agile_ref()</span> // <span style="text-decoration: line-through;">{</span> // <span style="text-decoration: line-through;"> if (m_cookie) {</span> // <span style="text-decoration: line-through;"> m_git-&gt;RevokeInterfaceFromGlobal(std::exchange(m_cookie, 0));</span> // <span style="text-decoration: line-through;"> }</span> // <span style="text-decoration: line-through;">}</span> </pre> <p>Since we are storing the cookie in a <code>unique_ptr</code>, we need to adjust the <code>empty</code> method:</p> <pre> bool empty() const noexcept { return <span style="border: solid 1px currentcolor;">reinterpret_cast&lt;uintptr_t&gt;(m_cookie.get())</span> != 0; } </pre> <p><b>Bonus chatter</b>: The Windows Implementation Library (wil) has a class similar to <code>unique_ptr</code> called <code>wil::unique_any</code> that lets you apply cleanup to any data type, not just a pointer.</p> <p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260807-00/?p=112597">Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 5</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/08/26, 01:38 AM

# Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 5 - The Old New Thing Source: [https://devblogs.microsoft.com/oldnewthing/20260807-00?p=112597](https://devblogs.microsoft.com/oldnewthing/20260807-00?p=112597) Last time, I confessed that[I lied when i said that we can’t use`std::unique\_ptr`to manage the registration cookie](https://devblogs.microsoft.com/oldnewthing/20260806-00/?p=112595)\. The trick here is that the registration cookie is of type`DWORD`, which fits in a pointer, so we can smuggle the integer value inside a pointer\. ``` template<typename T> struct fake_agile_ref { private: using Smart = std::conditional_t< std::is_base_of_v<winrt::Windows::Foundation::IUnknown, T>, T, winrt::com_ptr<T>>; struct git_deleter { winrt::com_ptr<IGlobalInterfaceTable> m_git;   void operator()(void* p) { m_git->RevokeInterfaceFromGlobal(static_cast<DWORD>(reinterpret_cast<uintptr_t>(p))); } }; winrt::com_ptr<IContextCallback> m_context; ULONG_PTR m_token = 0; std::unique_ptr<void, git_deleter> m_cookie; void* m_raw = nullptr; ``` Our custom deleter holds a pointer to the Global Interface Table and uses it to revoke the cookie on destruction\. The cookie is an integer smuggled inside a pointer, so we cast the pointer back to an integer by passing through a`uintptr\_t`to avoid a compiler warning about casting between an integer and pointer of different sizes\. We are relying on the fact that Windows implementations are required to support round\-tripping integers through pointers\. Macros like`MAKEINTRESOURCE`rely on it\. It’s also codified in Windows with helper functions like`PtrToInt`and`IntToPtr`, but I’m writing it out for expository purposes rather than using those helpers\. We can then store the Global Interface Table pointer and the corresponding cookie in the`unique\_ptr`: ``` 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(); auto& git = m_cookie.get_deleter().m_git; git = winrt::create_instance<IGlobalInterfaceTable>(CLSID_StdGlobalInterfaceTable); DWORD cookie; winrt::check_hresult(git->RegisterInterfaceInGlobal( winrt::make<force_marshal<Smart>>(p).get(), __uuidof(IUnknown), &m_cookie)); m_cookie.reset(reinterpret_cast<void*>(static_cast<uintptr_t>(cookie))); } } ``` And now that we are letting`unique\_ptr`manage the lifetime of the cookie, we don’t need a custom destructor, which allows us to use the Rule of Zero and simply not have any copy or move constructors or assignment operators\. ``` // fake_agile_ref(fake_agile_ref&& other) noexcept : // m_context(std::move(other.m_context)), // m_token(std:exchange(other.m_token, 0)), // m_git(std::move(other.m_git)), // m_cookie(std::exchange(other.m_cookie, 0)), // m_raw(other.m_raw) // { // } // fake_agile_ref& operator=(fake_agile_ref&& other) noexcept // { // using std::swap; // swap(m_context, other.m_context); // swap(m_token, other.m_token); // swap(m_git, other.m_git); // swap(m_cookie, other.m_cookie); // swap(m_raw, other.m_raw); // } // ~fake_agile_ref() // { // if (m_cookie) { // m_git->RevokeInterfaceFromGlobal(std::exchange(m_cookie, 0)); // } // } ``` Since we are storing the cookie in a`unique\_ptr`, we need to adjust the`empty`method: ``` bool empty() const noexcept { return reinterpret_cast<uintptr_t>(m_cookie.get()) != 0; } ``` **Bonus chatter**: The Windows Implementation Library \(wil\) has a class similar to`unique\_ptr`called`wil::unique\_any`that lets you apply cleanup to any data type, not just a pointer\. ### 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