{"id":112560,"date":"2026-07-23T07:00:00","date_gmt":"2026-07-23T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112560"},"modified":"2026-07-23T22:25:54","modified_gmt":"2026-07-24T05:25:54","slug":"20260723-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260723-00\/?p=112560","title":{"rendered":"Making an agile version of a Windows Runtime delegate in C++\/WinRT, part 4"},"content":{"rendered":"<p>Last time, we wrote a wrapper delegate that <a title=\"Making an agile version of a Windows Runtime delegate in C++\/WinRT, part 4\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260722-00\/?p=112552\"> checked whether the context it was being invoked from matched the context it was captured from<\/a>.<\/p>\n<pre>    if (d.try_as&lt;::INoMarshal&gt;()) {\r\n        return [d = std::forward&lt;Delegate&gt;(d),\r\n                context = winrt::capture&lt;IContextCallback&gt;(CoGetObjectContext)](auto&amp;&amp;...args) {\r\n            if (context == winrt::capture&lt;IContextCallback&gt;(CoGetObjectContext)) {\r\n                d(std::forward&lt;decltype(args)&gt;(args)...);\r\n            } else {\r\n                throw winrt::hresult_error(CO_E_NOT_SUPPORTED);\r\n            }\r\n        };\r\n    }\r\n<\/pre>\n<p>We did this by comparing context objects.<\/p>\n<p>This obtains the current object context in order to compare it with the original one, and that means an internal <code>Add\u00adRef<\/code>, and then we have to explicitly <code>Release<\/code> it.<\/p>\n<p>But there&#8217;s a way to do this without having to obtain any objects.<\/p>\n<p>The <code>Co\u00adGet\u00adContext\u00adToken<\/code> function gives you an integer that uniquely identifies a live context object. You can then compare integers instead of having to compare COM objects.<\/p>\n<p>Note that the context must be live. Once you allow the context to destruct, the value might be reused. (You&#8217;re already used to this. Process and thread IDs work the same way: They remain unique as long as they are running or you still have a reference to them by a <code>HANDLE<\/code>.)<\/p>\n<p>Since we are keeping the context alive by the <code>IContext\u00adCallback<\/code> returned by <code>Co\u00adGet\u00adObject\u00adContext<\/code>, we can pair that with a context token to make for faster checks in the future.<\/p>\n<pre><span style=\"border: solid 1px currentcolor; border-bottom: none;\">ULONG_PTR get_context_token()                       <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">{                                                   <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    ULONG_PTR token;                                <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    winrt::check_hresult(CoGetContextToken(&amp;token));<\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    return token;                                   <\/span>\r\n<span style=\"border: solid 1px currentcolor; border-top: none;\">}                                                   <\/span>\r\n\r\n    if (d.try_as&lt;::INoMarshal&gt;()) {\r\n        return [d = std::forward&lt;Delegate&gt;(d),\r\n                context = winrt::capture&lt;IContextCallback&gt;(CoGetObjectContext),\r\n                <span style=\"border: solid 1px currentcolor;\">token = get_context_token()<\/span>](auto&amp;&amp;...args) {\r\n            if (<span style=\"border: solid 1px currentcolor;\">token == get_context_token()<\/span>) {\r\n                d(std::forward&lt;decltype(args)&gt;(args)...);\r\n            } else {\r\n                throw winrt::hresult_error(CO_E_NOT_SUPPORTED);\r\n            }\r\n        };\r\n    }\r\n<\/pre>\n<p>Are we done?<\/p>\n<p>Of course not!<\/p>\n<p>There&#8217;s a flaw in the above code. More next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Optimizing the context check.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-112560","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Optimizing the context check.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112560","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=112560"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112560\/revisions"}],"predecessor-version":[{"id":112561,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112560\/revisions\/112561"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=112560"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112560"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112560"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}