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’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<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>>;
<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<IGlobalInterfaceTable> 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->RevokeInterfaceFromGlobal(static_cast<DWORD>(reinterpret_cast<uintptr_t>(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<IContextCallback> m_context;
ULONG_PTR m_token = 0;
<span style="border: solid 1px currentcolor;">std::unique_ptr<void, git_deleter> 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’s also codified in Windows with helper functions like <code>PtrToInt</code> and <code>IntToPtr</code>, but I’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& p) : m_raw(winrt::get_abi(p))
{
if (m_raw) {
m_context = winrt::capture<IContextCallback>(CoGetObjectContext);
m_token = get_context_token();
<span style="border: solid 1px currentcolor; border-bottom: none;">auto& git = m_cookie.get_deleter().m_git; </span>
<span style="border: 1px currentcolor; border-style: none solid;">git = winrt::create_instance<IGlobalInterfaceTable>(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>->RegisterInterfaceInGlobal(
winrt::make<force_marshal<Smart>>(p).get(),
__uuidof(IUnknown), &m_cookie));
<span style="border: solid 1px currentcolor;">m_cookie.reset(reinterpret_cast<void*>(static_cast<uintptr_t>(cookie)));</span>
}
}
</pre>
<p>And now that we are letting <code>unique_ptr</code> 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.</p>
<pre> // <span style="text-decoration: line-through;">fake_agile_ref(fake_agile_ref&& 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& operator=(fake_agile_ref&& 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->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<uintptr_t>(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>
# 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 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\.
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.
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.