用C++/WinRT制作敏捷版Windows运行时委托,第4部分
摘要
本文讨论在C++/WinRT中实现敏捷的Windows运行时委托,重点介绍使用CoGetContextToken比较上下文令牌以提高效率,而不是比较COM对象。
<p>上一次,我们编写了一个包装委托,<a title="用C++/WinRT制作敏捷版Windows运行时委托,第4部分" href="https://devblogs.microsoft.com/oldnewthing/20260722-00/?p=112552">检查它被调用的上下文是否与其被捕获的上下文匹配</a>。</p>
<pre> if (d.try_as<::INoMarshal>()) {
return [d = std::forward<Delegate>(d),
context = winrt::capture<IContextCallback>(CoGetObjectContext)](auto&&...args) {
if (context == winrt::capture<IContextCallback>(CoGetObjectContext)) {
d(std::forward<decltype(args)>(args)...);
} else {
throw winrt::hresult_error(CO_E_NOT_SUPPORTED);
}
};
}
</pre>
<p>我们通过比较上下文对象来实现这一点。</p>
<p>这会获取当前对象上下文以便与原始上下文进行比较,这意味着内部会调用<code>AddRef</code>,然后我们必须显式调用<code>Release</code>。</p>
<p>但有一种方法可以在不获取任何对象的情况下完成此操作。</p>
<p><code>CoGetContextToken</code>函数会返回一个唯一标识活动上下文对象的整数。然后你可以比较整数,而无需比较COM对象。</p>
<p>请注意,上下文必须是活动的。一旦允许上下文析构,该值可能会被重用。(你已经习惯这一点了。进程和线程ID以相同方式工作:只要它们仍在运行或者你通过<code>HANDLE</code>持有对它们的引用,它们就保持唯一。)</p>
<p>由于我们通过<code>CoGetObjectContext</code>返回的<code>IContextCallback</code>保持上下文活跃,我们可以将其与上下文令牌配对,以便将来进行更快的检查。</p>
<pre><span style="border: solid 1px currentcolor; border-bottom: none;">ULONG_PTR get_context_token() </span>
<span style="border: 1px currentcolor; border-style: none solid;">{ </span>
<span style="border: 1px currentcolor; border-style: none solid;"> ULONG_PTR token; </span>
<span style="border: 1px currentcolor; border-style: none solid;"> winrt::check_hresult(CoGetContextToken(&token));</span>
<span style="border: 1px currentcolor; border-style: none solid;"> return token; </span>
<span style="border: solid 1px currentcolor; border-top: none;">} </span>
if (d.try_as<::INoMarshal>()) {
return [d = std::forward<Delegate>(d),
context = winrt::capture<IContextCallback>(CoGetObjectContext),
<span style="border: solid 1px currentcolor;">token = get_context_token()</span>](auto&&...args) {
if (<span style="border: solid 1px currentcolor;">token == get_context_token()</span>) {
d(std::forward<decltype(args)>(args)...);
} else {
throw winrt::hresult_error(CO_E_NOT_SUPPORTED);
}
};
}
</pre>
<p>我们完成了吗?</p>
<p>当然没有!</p>
<p>上面的代码存在一个缺陷。下次继续。</p>
<p>这篇<a href="https://devblogs.microsoft.com/oldnewthing/20260723-00/?p=112560">用C++/WinRT制作敏捷版Windows运行时委托,第4部分</a>的文章最初出现在<a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>上。</p>
查看缓存全文
缓存时间: 2026/07/24 17:02
# 在 C++/WinRT 中创建敏捷版本的 Windows 运行时委托,第 4 部分 - 旧事新说
来源:https://devblogs.microsoft.com/oldnewthing/20260723-00?p=112560
上次,我们编写了一个包装委托,检查它被调用的上下文是否与它被捕获的上下文匹配(https://devblogs.microsoft.com/oldnewthing/20260722-00/?p=112552)。
`` if (d.try_as<::INoMarshal>()) { return [d = std::forward(d), context = winrt::capture(CoGetObjectContext)](auto&&...args) { if (context == winrt::capture(CoGetObjectContext)) { d(std::forward(args)...); } else { throw winrt::hresult_error(CO_E_NOT_SUPPORTED); } }; } ``
我们通过比较上下文对象来实现这一点。这会获取当前对象上下文以与原始上下文进行比较,这意味着内部会执行一次 `AddRef`,然后我们必须显式地 `Release` 它。但有一种方法可以不必获取任何对象就能做到。
`CoGetContextToken` 函数会返回一个整数,该整数唯一标识一个活动的上下文对象。然后你可以比较整数,而不是比较 COM 对象。注意,上下文必须是活动的。一旦你让上下文析构,该值可能会被重用。(你已经习惯了这一点。进程 ID 和线程 ID 的工作方式类似:只要它们还在运行,或者你通过 `HANDLE` 保留了引用,它们就保持唯一。)
由于我们通过 `CoGetObjectContext` 返回的 `IContextCallback` 保持上下文活动,我们可以将其与上下文令牌配对,以便将来进行更快的检查。
`` ULONG_PTR get_context_token() { ULONG_PTR token; winrt::check_hresult(CoGetContextToken(&token)); return token; } if (d.try_as<::INoMarshal>()) { return [d = std::forward(d), context = winrt::capture(CoGetObjectContext), token = get_context_token()](auto&&...args) { if (token == get_context_token()) { d(std::forward(args)...); } else { throw winrt::hresult_error(CO_E_NOT_SUPPORTED); } }; } ``
我们完成了?当然没有!上面的代码存在一个缺陷。下次继续。
### 分类
### 主题
## 作者
Raymond Chen
Raymond 参与 Windows 的演进已有 30 多年。2003 年,他创办了一个名为“旧事新说”的网站,其受欢迎程度远远超出他最疯狂的想象——这一发展至今仍让他感到毛骨悚然。该网站催生了一本书,巧合的是书名也叫《旧事新说》(Addison Wesley, 2007)。他偶尔会出现在 Windows Dev Docs 的 Twitter 账号上,讲述一些不传递有用信息的故事。
相似文章
在 C++/WinRT 中制作 Windows Runtime 委托的敏捷版本,第5部分
本文继续系列文章,介绍如何在 C++/WinRT 中实现敏捷委托,解决在正确上下文中销毁非敏捷委托的问题,方法是使用自定义删除器和 IContextCallback。
在C++/WinRT中创建Windows运行时代理的敏捷版本,第3部分
这篇博客文章讨论了在C++/WinRT中处理实现INoMarshal接口的Windows运行时代理,提供了一个敏捷的代理包装器,通过检查调用上下文来避免封送错误。
在 C++/WinRT 中创建 Windows Runtime 委托的敏捷版本,第一部分
本文介绍如何通过将委托包装在 agile_ref 中来在 C++/WinRT 中创建 Windows Runtime 委托的敏捷版本,并承诺在第二部分中提供更多细节。
在 C++/WinRT 中创建 Windows Runtime 委托的敏捷版本,第 9 部分
Raymond Chen 继续他的系列文章,讨论在 C++/WinRT 中创建敏捷版 Windows Runtime 委托,并比较 C++/WinRT、C++/CX 和 WRL 如何处理不可封送委托和敏捷引用创建。
在C++/WinRT中创建Windows Runtime委托的敏捷版本,第8部分
本文讨论了在C++/WinRT中创建敏捷委托时修复异常安全性问题,解决了未指定lambda捕获构造顺序导致的引用泄漏。