{"id":107746,"date":"2023-01-24T07:00:00","date_gmt":"2023-01-24T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107746"},"modified":"2023-01-23T18:26:11","modified_gmt":"2023-01-24T02:26:11","slug":"20230124-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230124-00\/?p=107746","title":{"rendered":"Inside C++\/WinRT: Apartment switching: The basic idea"},"content":{"rendered":"<p>One of the features of C++\/WinRT is that if you <code>co_await<\/code> an IAsyncAction or <code>IAsyncOperation<\/code>, the C++\/WinRT library returns to the original COM apartment before resuming the coroutine. This behavior is generally desirable because you expect that COM objects prior to performing a <code>co_await<\/code> are still usable after it returns.<\/p>\n<p>This task is accomplished <a title=\"Using contexts to return to a COM apartment later\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191129-00\/?p=103162\"> with the assistance of <code>IContextCallback<\/code><\/a>.<\/p>\n<p>Here&#8217;s the basic idea:\u00b9<\/p>\n<pre>inline int32_t __stdcall resume_apartment_callback(\r\n    com_callback_args* args) noexcept\r\n{\r\n    coroutine_handle&lt;&gt;::from_address(args-&gt;data)();\r\n    return 0;\r\n};\r\n\r\nvoid resume_apartment(\r\n    com_ptr&lt;IContextCallback&gt; const&amp; context,\r\n    std::coroutine_handle&lt;&gt; handle)\r\n{\r\n    com_callback_args args{};\r\n    args.data = handle.address();\r\n\r\n    check_hresult(\r\n        context-&gt;ContextCallback(resume_apartment_callback,\r\n            &amp;args,\r\n            guid_of&lt;ICallbackWithNoReentrancyToApplicationSTA&gt;(),\r\n            5, nullptr));\r\n}\r\n<\/pre>\n<p>To resume a coroutine synchronously in a particular context, we use the <code>IContext\u00adCallback::<wbr \/>Context\u00adCallback<\/code> method to ask COM to run a particular function in that desired context. We convert the coroutine handle to a pointer to use as our reference data, and in the callback, we convert the pointer back to a coroutine handle so we can invoke it, thereby resuming the coroutine.<\/p>\n<p>We can use this to build the <code>apartment_<wbr \/>context<\/code> object.<\/p>\n<pre>struct apartment_context\r\n{\r\n    apartment_context() = default;\r\n    apartment_context(std::nullptr_t) : context(nullptr) { }\r\n\r\n    operator bool() const noexcept { return context != nullptr; }\r\n    bool operator!() const noexcept { return context == nullptr; }\r\n\r\n    com_ptr&lt;IContextCallback&gt; context =\r\n            capture&lt;IContextCallback&gt;(WINRT_IMPL_CoGetObjectContext);\r\n};\r\n\r\nstruct apartment_awaiter\r\n{\r\n    apartment_context const&amp; context;\r\n\r\n    bool await_ready() const noexcept\r\n    {\r\n        return false;\r\n    }\r\n\r\n    void await_suspend(coroutine_handle&lt;&gt; handle)\r\n    {\r\n        apartment_context extend_lifetime = context;\r\n        resume_apartment(context.context, handle);\r\n    }\r\n\r\n    void await_resume() const noexcept\r\n    {\r\n    }\r\n};\r\n\r\napartment_awaiter operator co_await(apartment_context const&amp; context)\r\n{\r\n    return { context };\r\n}\r\n<\/pre>\n<p>To construct an <code>apartment_<wbr \/>context<\/code>, we call <code>Co\u00adGet\u00adObject\u00adContext<\/code> (through the C++\/WinRT alias) to obtain an <code>IContext\u00adCallback<\/code>.<\/p>\n<p>There is also a <code>nullptr<\/code> constructor if you want to declare an empty <code>apartment_context<\/code>. Empty contexts aren&#8217;t usable, but they are useful: They let you declare a variable and initialize it with a proper context later.<\/p>\n<p>To <code>co_await<\/code> an <code>apartment_<wbr \/>context<\/code>, we construct an <code>apartment_<wbr \/>awaiter<\/code> which remembers the context being awaited, and the <code>await_<wbr \/>suspend<\/code> method uses it to call <code>resume_<wbr \/>apartment()<\/code>.<\/p>\n<p>We can now add COM context support to <a title=\"Inside C++\/WinRT: Coroutine completions: The oversimplified version\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230123-00\/?p=107742\"> our oversimplified Windows Runtime awaiter<\/a>.<\/p>\n<pre>template &lt;typename Async&gt;\r\nstruct await_adapter\r\n{\r\n    await_adapter(Async const&amp; async) : async(async) { }\r\n\r\n    Async const&amp; async;\r\n\r\n    bool await_ready() const noexcept\r\n    {\r\n        return false;\r\n    }\r\n\r\n    void await_suspend(std::experimental::coroutine_handle&lt;&gt; handle) const\r\n    {\r\n        auto extend_lifetime = async;\r\n        async.Completed([\r\n            handle,\r\n            <span style=\"color: #08f;\">context = apartment_context()<\/span>\r\n        ](auto&amp;&amp; ...)\r\n        {\r\n            <span style=\"color: #08f;\">resume_apartment(context.context, handle);<\/span>\r\n        });\r\n    }\r\n\r\n    auto await_resume() const\r\n    {\r\n        return async.GetResults();\r\n    }\r\n};\r\n<\/pre>\n<p>We capture an <code>apartment_<wbr \/>context<\/code> in the lambda and use <code>resume_<wbr \/>apartment()<\/code> to resume the coroutine in that captured context.<\/p>\n<p>This code is still flawed, though. We&#8217;ll continue the discussion next time.<\/p>\n<p>\u00b9 The C++\/WinRT library does not <code>#include &lt;windows.h&gt;<\/code>. All of the dependencies on Windows are wrapped inside parallel declarations within the C++\/WinRT library. The <code>com_<wbr \/>callback_<wbr \/>args<\/code> structure, for example, is an ABI-equivalent version of the <code>ComCallData<\/code> structure.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Getting back to where you started.<\/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-107746","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Getting back to where you started.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107746","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=107746"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107746\/revisions"}],"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=107746"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107746"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107746"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}